#include "pch.h" #include "SvgView.h" #if __has_include("SvgView.g.cpp") #include "SvgView.g.cpp" #endif #include #ifdef USE_WINUI3 #include #include #else #include #include #endif #include "D2DDevice.h" #include "D2DDeviceContext.h" #include "GroupView.h" #include using namespace winrt; namespace winrt::RNSVG::implementation { SvgView::SvgView(IReactContext const &context) : m_reactContext(context) { uint32_t creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; 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 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 dxgiDevice{d3dDevice.as()}; // Create the Direct2D device and a corresponding context. com_ptr device; check_hresult(D2D1CreateDevice(dxgiDevice.get(), nullptr, device.put())); m_device = make(device); com_ptr deviceContext; check_hresult(device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, deviceContext.put())); m_deviceContext = make(deviceContext); m_panelLoadedRevoker = Loaded(winrt::auto_revoke, {get_weak(), &SvgView::Panel_Loaded}); m_panelUnloadedRevoker = Unloaded(winrt::auto_revoke, {get_weak(), &SvgView::Panel_Unloaded}); } void SvgView::SvgParent(xaml::FrameworkElement const &value) { if (value) { m_panelUnloadedRevoker.revoke(); m_parent = value; } } void SvgView::UpdateProperties(IJSValueReader const &reader, bool forceUpdate, bool invalidate) { // If forceUpdate is false, that means this is a nested Svg // and we're inheriting props. Pass those along to the group. if (!forceUpdate && m_group) { m_group.UpdateProperties(reader, forceUpdate, invalidate); } else { auto const &propertyMap{JSValueObject::ReadFrom(reader)}; for (auto const &pair : propertyMap) { auto const &propertyName{pair.first}; auto const &propertyValue{pair.second}; if (propertyName == "name") { if (m_parent && m_group) { m_group.SvgRoot().Templates().Remove(m_id); } m_id = to_hstring(Utils::JSValueAsString(propertyValue)); if (m_parent) { SaveDefinition(); } } else if (propertyName == "width") { m_width = propertyValue.To(); } else if (propertyName == "height") { m_height = propertyValue.To(); } else if (propertyName == "bbWidth") { m_bbWidth = propertyValue.To(); Width(m_bbWidth.Value); } else if (propertyName == "bbHeight") { m_bbHeight = propertyValue.To(); Height(m_bbHeight.Value); } 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); } else if (propertyName == "color") { m_currentColor = Utils::JSValueAsColor(propertyValue); } else if (propertyName == "responsible") { m_isResponsible = propertyValue.AsBoolean(); } } Invalidate(); } } Size SvgView::MeasureOverride(Size const &availableSize) { for (auto const &child : Children()) { child.Measure(availableSize); } return availableSize; } Size SvgView::ArrangeOverride(Size const &finalSize) { for (auto const &child : Children()) { child.Arrange({0, 0, finalSize.Width, finalSize.Height}); } return finalSize; } void SvgView::Panel_Loaded(IInspectable const &sender, xaml::RoutedEventArgs const & ) { if (auto const &svgView{sender.try_as()}) { if (!m_loaded) { m_loaded = true; svgView.CreateResources(); } } } void SvgView::Panel_Unloaded(IInspectable const &sender, xaml::RoutedEventArgs const & ) { if (auto const &svgView{sender.try_as()}) { svgView.Unload(); } } winrt::Windows::Foundation::Size SvgView::CanvasSize() noexcept { return ActualSize(); } void SvgView::SaveDefinition() { if (m_id != L"" && m_group) { m_group.SvgRoot().Templates().Insert(m_id, *this); m_group.SaveDefinition(); } } void SvgView::MergeProperties(RNSVG::IRenderable const &other) { if (m_group) { m_group.MergeProperties(other); } } void SvgView::Draw(RNSVG::D2DDeviceContext const &context, Size const &size) { com_ptr deviceContext{get_self(context)->Get()}; 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 (SvgParent()) { width = Utils::GetAbsoluteLength(m_bbWidth, width); height = Utils::GetAbsoluteLength(m_bbHeight, height); } Rect elRect{0, 0, width, height}; deviceContext->SetTransform(Utils::GetViewBoxTransformD2D(vbRect, elRect, m_align, m_meetOrSlice) * transform); } if (m_group) { m_group.SaveDefinition(); m_group.Draw(context, size); } if (!m_hasRendered) { m_hasRendered = true; } deviceContext->SetTransform(transform); } void SvgView::CreateGeometry(RNSVG::D2DDeviceContext const &context) { if (m_group) { m_group.CreateGeometry(context); } } void SvgView::CreateResources() { if (m_group) { 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::Unload() { m_reactContext = nullptr; m_templates.Clear(); m_brushes.Clear(); if (m_group) { m_group.Unload(); } } void SvgView::Invalidate() { m_brushes.Clear(); m_templates.Clear(); if (!m_loaded) { return; } com_ptr deviceContext{get_self(DeviceContext())->Get()}; Size size{static_cast(ActualWidth()), static_cast(ActualHeight())}; xaml::Media::Imaging::SurfaceImageSource surfaceImageSource( static_cast(size.Width), static_cast(size.Height)); com_ptr sisNativeWithD2D{surfaceImageSource.as()}; // Associate the Direct2D device with the SurfaceImageSource. com_ptr device{get_self(Device())->Get()}; sisNativeWithD2D->SetDevice(device.get()); com_ptr 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(size.Width), static_cast(size.Height)}; POINT offset{0, 0}; check_hresult(sisNativeWithD2D->BeginDraw(updateRect, __uuidof(IDXGISurface), dxgiSurface.put_void(), &offset)); // Create render target. com_ptr 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(offset.x), static_cast(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