From 7b9bf64d8dd19db3b9c966ca1aeb34738121b014 Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Mon, 9 Jan 2023 16:41:39 -0800 Subject: [PATCH 1/5] Change files --- ...ative-windows-17404ba8-a05f-44cf-b955-402a2d2aec67.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/react-native-windows-17404ba8-a05f-44cf-b955-402a2d2aec67.json diff --git a/change/react-native-windows-17404ba8-a05f-44cf-b955-402a2d2aec67.json b/change/react-native-windows-17404ba8-a05f-44cf-b955-402a2d2aec67.json new file mode 100644 index 00000000000..93d35f46888 --- /dev/null +++ b/change/react-native-windows-17404ba8-a05f-44cf-b955-402a2d2aec67.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "[Fabric][Image] Add tintColor, blurRadius, and resizeMode = repeat", + "packageName": "react-native-windows", + "email": "email not defined", + "dependentChangeType": "patch" +} From f3391ba631138e80f7aa35aac62c89f1c9bf8749 Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Mon, 9 Jan 2023 16:42:15 -0800 Subject: [PATCH 2/5] [Fabric][Image] Add tintColor, blurRadius, and resizeMode = repeat --- .../Fabric/Composition/ImageComponentView.cpp | 128 ++++++++++++++---- 1 file changed, 100 insertions(+), 28 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp index c57fd972926..5a95366b5a9 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -203,8 +204,25 @@ void ImageComponentView::ensureDrawingSurface() noexcept { winrt::check_hresult(m_wicbmp->GetSize(&width, &height)); if (!m_drawingSurface && m_wicbmp) { + winrt::Windows::Foundation::Size drawingSurfaceSize{static_cast(width), static_cast(height)}; + + const auto imageProps = std::static_pointer_cast(m_props); + const auto frame{m_layoutMetrics.getContentFrame().size}; + + if (imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat) { + drawingSurfaceSize = {frame.width, frame.height}; + } else if (imageProps->blurRadius > 0) { + // The output of this effect can be larger than the input bitmap based on the blur radius and the border mode. + // If the border mode is set to D2D1_BORDER_MODE_SOFT (the default value) + // the size of the output bitmap increases by the size of the blur kernel, represented in pixels. + // This table provides an equation that you can use to compute the output bitmap. + // Output bitmap growth (X and Y) = StandardDeviation (DIPs)*6*((User DPI)/96) + const auto bmpGrowth{imageProps->blurRadius * 6 * m_layoutMetrics.pointScaleFactor}; + drawingSurfaceSize = {drawingSurfaceSize.Width + bmpGrowth, drawingSurfaceSize.Height + bmpGrowth}; + } + m_drawingSurface = m_compContext.CreateDrawingSurface( - {static_cast(width), static_cast(height)}, + drawingSurfaceSize, winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized, winrt::Windows::Graphics::DirectX::DirectXAlphaMode::Premultiplied); @@ -212,8 +230,7 @@ void ImageComponentView::ensureDrawingSurface() noexcept { auto surfaceBrush = m_compContext.CreateSurfaceBrush(m_drawingSurface); - const auto &imageProps = *std::static_pointer_cast(m_props); - switch (imageProps.resizeMode) { + switch (imageProps->resizeMode) { case facebook::react::ImageResizeMode::Stretch: surfaceBrush.Stretch(winrt::Microsoft::ReactNative::Composition::CompositionStretch::Fill); break; @@ -223,16 +240,21 @@ void ImageComponentView::ensureDrawingSurface() noexcept { case facebook::react::ImageResizeMode::Contain: surfaceBrush.Stretch(winrt::Microsoft::ReactNative::Composition::CompositionStretch::Uniform); break; - case facebook::react::ImageResizeMode::Center: - surfaceBrush.Stretch(winrt::Microsoft::ReactNative::Composition::CompositionStretch::None); - break; case facebook::react::ImageResizeMode::Repeat: - surfaceBrush.Stretch(winrt::Microsoft::ReactNative::Composition::CompositionStretch::UniformToFill); - // TODO - Hook up repeat + surfaceBrush.HorizontalAlignmentRatio(0.0f); + surfaceBrush.VerticalAlignmentRatio(0.0f); + [[fallthrough]]; + case facebook::react::ImageResizeMode::Center: { + surfaceBrush.Stretch( + (height < frame.height && width < frame.width) + ? winrt::Microsoft::ReactNative::Composition::CompositionStretch::None + : winrt::Microsoft::ReactNative::Composition::CompositionStretch::Uniform); break; + } default: assert(false); } + m_visual.Brush(surfaceBrush); } } @@ -250,8 +272,6 @@ void ImageComponentView::DrawImage() noexcept { m_drawingSurface.as(drawingSurfaceInterop); if (CheckForDeviceRemoved(drawingSurfaceInterop->BeginDraw(d2dDeviceContext.put(), &offset))) { - const auto ¶graphProps = *std::static_pointer_cast(m_props); - winrt::com_ptr bitmap; winrt::check_hresult(d2dDeviceContext->CreateBitmapFromWicBitmap(m_wicbmp.get(), nullptr, bitmap.put())); @@ -260,24 +280,76 @@ void ImageComponentView::DrawImage() noexcept { d2dDeviceContext->Clear(m_props->backgroundColor.AsD2DColor()); } - UINT width, height; - winrt::check_hresult(m_wicbmp->GetSize(&width, &height)); - - D2D1_RECT_F rect = D2D1::RectF( - static_cast(offset.x / m_layoutMetrics.pointScaleFactor), - static_cast(offset.y / m_layoutMetrics.pointScaleFactor), - static_cast((offset.x + width) / m_layoutMetrics.pointScaleFactor), - static_cast((offset.y + height) / m_layoutMetrics.pointScaleFactor)); - - const auto dpi = m_layoutMetrics.pointScaleFactor * 96.0f; - float oldDpiX, oldDpiY; - d2dDeviceContext->GetDpi(&oldDpiX, &oldDpiY); - d2dDeviceContext->SetDpi(dpi, dpi); - - d2dDeviceContext->DrawBitmap(bitmap.get(), rect); - - // Restore old dpi setting - d2dDeviceContext->SetDpi(oldDpiX, oldDpiY); + const auto imageProps = std::static_pointer_cast(m_props); + + bool useEffects{ + imageProps->blurRadius > 0 || isColorMeaningful(imageProps->tintColor) || + imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat}; + + if (useEffects) { + winrt::com_ptr bitmapEffects; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1BitmapSource, bitmapEffects.put())); + winrt::check_hresult(bitmapEffects->SetValue(D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE, m_wicbmp.get())); + + if (imageProps->blurRadius > 0) { + winrt::com_ptr gaussianBlurEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1GaussianBlur, gaussianBlurEffect.put())); + winrt::check_hresult(gaussianBlurEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, imageProps->blurRadius)); + gaussianBlurEffect->SetInputEffect(0, bitmapEffects.get()); + bitmapEffects.copy_from(gaussianBlurEffect.get()); + } + + if (isColorMeaningful(imageProps->tintColor)) { + winrt::com_ptr tintColorEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Flood, tintColorEffect.put())); + winrt::check_hresult(tintColorEffect->SetValue(D2D1_FLOOD_PROP_COLOR, imageProps->tintColor.AsD2DColor())); + + winrt::com_ptr compositeEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Composite, compositeEffect.put())); + winrt::check_hresult(compositeEffect->SetValue(D2D1_COMPOSITE_PROP_MODE, D2D1_COMPOSITE_MODE_SOURCE_IN)); + compositeEffect->SetInputEffect(0, bitmapEffects.get()); + compositeEffect->SetInputEffect(1, tintColorEffect.get()); + + bitmapEffects.copy_from(compositeEffect.get()); + } + + if (imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat) { + winrt::com_ptr borderEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Border, borderEffect.put())); + winrt::check_hresult(borderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_WRAP)); + winrt::check_hresult(borderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_WRAP)); + borderEffect->SetInputEffect(0, bitmapEffects.get()); + + d2dDeviceContext->DrawImage(borderEffect.get()); + } else { + winrt::com_ptr image; + bitmapEffects->GetOutput(image.put()); + + D2D1_RECT_F imageBounds; + winrt::check_hresult(d2dDeviceContext->GetImageLocalBounds(image.get(), &imageBounds)); + + d2dDeviceContext->DrawImage(bitmapEffects.get(), {static_cast(offset.x), static_cast(offset.y)}, imageBounds); + } + } else { + UINT width, height; + winrt::check_hresult(m_wicbmp->GetSize(&width, &height)); + + D2D1_RECT_F rect = D2D1::RectF( + static_cast(offset.x / m_layoutMetrics.pointScaleFactor), + static_cast(offset.y / m_layoutMetrics.pointScaleFactor), + static_cast((offset.x + width) / m_layoutMetrics.pointScaleFactor), + static_cast((offset.y + height) / m_layoutMetrics.pointScaleFactor)); + + const auto dpi = m_layoutMetrics.pointScaleFactor * 96.0f; + float oldDpiX, oldDpiY; + d2dDeviceContext->GetDpi(&oldDpiX, &oldDpiY); + d2dDeviceContext->SetDpi(dpi, dpi); + + d2dDeviceContext->DrawBitmap(bitmap.get(), rect); + + // Restore old dpi setting + d2dDeviceContext->SetDpi(oldDpiX, oldDpiY); + } // Our update is done. EndDraw never indicates rendering device removed, so any // failure here is unexpected and, therefore, fatal. From 7e20e1746187db13d3b0502a2a9581a79a7d870f Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Tue, 10 Jan 2023 09:41:35 -0800 Subject: [PATCH 3/5] formatting --- .../Fabric/Composition/ImageComponentView.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp index 5a95366b5a9..843a893eba0 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp @@ -281,7 +281,7 @@ void ImageComponentView::DrawImage() noexcept { } const auto imageProps = std::static_pointer_cast(m_props); - + bool useEffects{ imageProps->blurRadius > 0 || isColorMeaningful(imageProps->tintColor) || imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat}; @@ -294,7 +294,8 @@ void ImageComponentView::DrawImage() noexcept { if (imageProps->blurRadius > 0) { winrt::com_ptr gaussianBlurEffect; winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1GaussianBlur, gaussianBlurEffect.put())); - winrt::check_hresult(gaussianBlurEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, imageProps->blurRadius)); + winrt::check_hresult( + gaussianBlurEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, imageProps->blurRadius)); gaussianBlurEffect->SetInputEffect(0, bitmapEffects.get()); bitmapEffects.copy_from(gaussianBlurEffect.get()); } @@ -328,7 +329,8 @@ void ImageComponentView::DrawImage() noexcept { D2D1_RECT_F imageBounds; winrt::check_hresult(d2dDeviceContext->GetImageLocalBounds(image.get(), &imageBounds)); - d2dDeviceContext->DrawImage(bitmapEffects.get(), {static_cast(offset.x), static_cast(offset.y)}, imageBounds); + d2dDeviceContext->DrawImage( + bitmapEffects.get(), {static_cast(offset.x), static_cast(offset.y)}, imageBounds); } } else { UINT width, height; From 0f907b05f4325c852bab8d500962da10a72f04e8 Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Fri, 20 Jan 2023 15:34:20 -0800 Subject: [PATCH 4/5] fix blurRadius --- .../Fabric/Composition/ImageComponentView.cpp | 564 +++++++++--------- 1 file changed, 287 insertions(+), 277 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp index 843a893eba0..96929be3ba5 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp @@ -22,215 +22,218 @@ #include #include "CompositionHelpers.h" -extern "C" HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT SDKVersion, IWICImagingFactory **ppIWICImagingFactory); +extern "C" HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT SDKVersion, IWICImagingFactory * *ppIWICImagingFactory); namespace Microsoft::ReactNative { -ImageComponentView::WindowsImageResponseObserver::WindowsImageResponseObserver( + ImageComponentView::WindowsImageResponseObserver::WindowsImageResponseObserver( std::shared_ptr image) { - m_image = image; -} + m_image = image; + } -void ImageComponentView::WindowsImageResponseObserver::didReceiveProgress(float progress) const { - // TODO progress? -} + void ImageComponentView::WindowsImageResponseObserver::didReceiveProgress(float progress) const { + // TODO progress? + } -void ImageComponentView::WindowsImageResponseObserver::didReceiveImage( - facebook::react::ImageResponse const &imageResponse) const { - auto sharedwicbmp = std::static_pointer_cast>(imageResponse.getImage()); - m_image->m_context.UIDispatcher().Post( + void ImageComponentView::WindowsImageResponseObserver::didReceiveImage( + facebook::react::ImageResponse const& imageResponse) const { + auto sharedwicbmp = std::static_pointer_cast>(imageResponse.getImage()); + m_image->m_context.UIDispatcher().Post( [wicbmp = *sharedwicbmp, image = m_image]() { image->didReceiveImage(wicbmp); }); -} + } -void ImageComponentView::WindowsImageResponseObserver::didReceiveFailure() const { - m_image->didReceiveFailureFromObserver(); -} + void ImageComponentView::WindowsImageResponseObserver::didReceiveFailure() const { + m_image->didReceiveFailureFromObserver(); + } -ImageComponentView::ImageComponentView( - const winrt::Microsoft::ReactNative::Composition::ICompositionContext &compContext, + ImageComponentView::ImageComponentView( + const winrt::Microsoft::ReactNative::Composition::ICompositionContext& compContext, facebook::react::Tag tag, - winrt::Microsoft::ReactNative::ReactContext const &reactContext) + winrt::Microsoft::ReactNative::ReactContext const& reactContext) : Super(compContext, tag), m_context(reactContext) { - static auto const defaultProps = std::make_shared(); - m_props = defaultProps; -} - -std::vector -ImageComponentView::supplementalComponentDescriptorProviders() noexcept { - return {}; -} - -void ImageComponentView::mountChildComponentView(const IComponentView &childComponentView, uint32_t index) noexcept { - assert(false); -} - -void ImageComponentView::unmountChildComponentView(const IComponentView &childComponentView, uint32_t index) noexcept { - assert(false); -} - -void ImageComponentView::ImageLoadStart() noexcept { - auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); - if (imageEventEmitter) { - imageEventEmitter->onLoadStart(); + static auto const defaultProps = std::make_shared(); + m_props = defaultProps; + } + + std::vector + ImageComponentView::supplementalComponentDescriptorProviders() noexcept { + return {}; + } + + void ImageComponentView::mountChildComponentView(const IComponentView& childComponentView, uint32_t index) noexcept { + assert(false); } -} -void ImageComponentView::didReceiveImage(const winrt::com_ptr &wicbmp) noexcept { - // TODO check for recycled? + void ImageComponentView::unmountChildComponentView(const IComponentView& childComponentView, uint32_t index) noexcept { + assert(false); + } - auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); - if (imageEventEmitter) { - imageEventEmitter->onLoad(); - imageEventEmitter->onLoadEnd(); + void ImageComponentView::ImageLoadStart() noexcept { + auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); + if (imageEventEmitter) { + imageEventEmitter->onLoadStart(); + } } - // TODO - handle m_props.tintColor, imageProps.resizeMode, imageProps.capInsets, imageProps.blurRadius + void ImageComponentView::didReceiveImage(const winrt::com_ptr& wicbmp) noexcept { + // TODO check for recycled? + + auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); + if (imageEventEmitter) { + imageEventEmitter->onLoad(); + imageEventEmitter->onLoadEnd(); + } + + // TODO - handle imageProps.capInsets #ifdef DEBUG - auto uiDispatcher = m_context.UIDispatcher(); - assert(uiDispatcher.HasThreadAccess()); + auto uiDispatcher = m_context.UIDispatcher(); + assert(uiDispatcher.HasThreadAccess()); #endif - m_wicbmp = wicbmp; - ensureDrawingSurface(); -} + m_wicbmp = wicbmp; + ensureDrawingSurface(); + } -void ImageComponentView::didReceiveFailureFromObserver() noexcept { - auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); - if (imageEventEmitter) { - imageEventEmitter->onError(); - imageEventEmitter->onLoadEnd(); + void ImageComponentView::didReceiveFailureFromObserver() noexcept { + auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); + if (imageEventEmitter) { + imageEventEmitter->onError(); + imageEventEmitter->onLoadEnd(); + } } -} -void ImageComponentView::updateProps( - facebook::react::Props::Shared const &props, - facebook::react::Props::Shared const &oldProps) noexcept { - const auto &oldImageProps = *std::static_pointer_cast(m_props); - const auto &newImageProps = *std::static_pointer_cast(props); + void ImageComponentView::updateProps( + facebook::react::Props::Shared const& props, + facebook::react::Props::Shared const& oldProps) noexcept { + const auto &oldImageProps = *std::static_pointer_cast(m_props); + const auto &newImageProps = *std::static_pointer_cast(props); - ensureVisual(); + ensureVisual(); - updateBorderProps(oldImageProps, newImageProps); + updateBorderProps(oldImageProps, newImageProps); - if (oldImageProps.backgroundColor != newImageProps.backgroundColor) { - m_drawingSurface = nullptr; // TODO dont need to recreate the surface just to redraw... - } + if (oldImageProps.backgroundColor != newImageProps.backgroundColor + || oldImageProps.blurRadius != newImageProps.blurRadius + || oldImageProps.tintColor != newImageProps.tintColor + || oldImageProps.resizeMode != newImageProps.resizeMode) { + m_drawingSurface = nullptr; // TODO dont need to recreate the surface just to redraw... + } - if (oldImageProps.opacity != newImageProps.opacity) { - m_visual.Opacity(newImageProps.opacity); - } + if (oldImageProps.opacity != newImageProps.opacity) { + m_visual.Opacity(newImageProps.opacity); + } - m_props = std::static_pointer_cast(props); -} + m_props = std::static_pointer_cast(props); + } -void ImageComponentView::updateState( - facebook::react::State::Shared const &state, - facebook::react::State::Shared const &oldState) noexcept { - auto oldImageState = std::static_pointer_cast(m_state); - auto newImageState = std::static_pointer_cast(state); + void ImageComponentView::updateState( + facebook::react::State::Shared const& state, + facebook::react::State::Shared const& oldState) noexcept { + auto oldImageState = std::static_pointer_cast(m_state); + auto newImageState = std::static_pointer_cast(state); - if (!m_imageResponseObserver) { - // Should ViewComponents enable_shared_from_this? then we dont need this dance to get a shared_ptr - std::shared_ptr fabricuiManager = + if (!m_imageResponseObserver) { + // Should ViewComponents enable_shared_from_this? then we dont need this dance to get a shared_ptr + std::shared_ptr fabricuiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties()); - auto componentViewDescriptor = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(m_tag); + auto componentViewDescriptor = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(m_tag); - m_imageResponseObserver = std::make_shared( + m_imageResponseObserver = std::make_shared( std::static_pointer_cast(componentViewDescriptor.view)); - } + } - setStateAndResubscribeImageResponseObserver(newImageState); - bool havePreviousData = oldImageState && oldImageState->getData().getImageSource() != facebook::react::ImageSource{}; + setStateAndResubscribeImageResponseObserver(newImageState); + bool havePreviousData = oldImageState && oldImageState->getData().getImageSource() != facebook::react::ImageSource{}; - if (!havePreviousData || + if (!havePreviousData || (newImageState && newImageState->getData().getImageSource() != oldImageState->getData().getImageSource())) { - // Loading actually starts a little before this, but this is the first time we know - // the image is loading and can fire an event from this component - std::static_pointer_cast(m_eventEmitter)->onLoadStart(); + // Loading actually starts a little before this, but this is the first time we know + // the image is loading and can fire an event from this component + std::static_pointer_cast(m_eventEmitter)->onLoadStart(); + } } -} -void ImageComponentView::setStateAndResubscribeImageResponseObserver( - facebook::react::ImageShadowNode::ConcreteState::Shared const &state) noexcept { - if (m_state) { - auto &observerCoordinator = m_state->getData().getImageRequest().getObserverCoordinator(); - observerCoordinator.removeObserver(*m_imageResponseObserver); + void ImageComponentView::setStateAndResubscribeImageResponseObserver( + facebook::react::ImageShadowNode::ConcreteState::Shared const& state) noexcept { + if (m_state) { + auto& observerCoordinator = m_state->getData().getImageRequest().getObserverCoordinator(); + observerCoordinator.removeObserver(*m_imageResponseObserver); + } + + m_state = state; + + if (m_state) { + auto& observerCoordinator = m_state->getData().getImageRequest().getObserverCoordinator(); + observerCoordinator.addObserver(*m_imageResponseObserver); + } } - m_state = state; + void ImageComponentView::updateLayoutMetrics( + facebook::react::LayoutMetrics const& layoutMetrics, + facebook::react::LayoutMetrics const& oldLayoutMetrics) noexcept { + // Set Position & Size Properties + + if ((layoutMetrics.displayType != m_layoutMetrics.displayType)) { + m_visual.IsVisible(layoutMetrics.displayType != facebook::react::DisplayType::None); + } + + updateBorderLayoutMetrics(layoutMetrics, *m_props); + + m_layoutMetrics = layoutMetrics; - if (m_state) { - auto &observerCoordinator = m_state->getData().getImageRequest().getObserverCoordinator(); - observerCoordinator.addObserver(*m_imageResponseObserver); + UpdateCenterPropertySet(); + m_visual.Size( + { layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor, + layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor }); + m_visual.Offset({ + layoutMetrics.frame.origin.x * layoutMetrics.pointScaleFactor, + layoutMetrics.frame.origin.y * layoutMetrics.pointScaleFactor, + 0.0f, + }); } -} -void ImageComponentView::updateLayoutMetrics( - facebook::react::LayoutMetrics const &layoutMetrics, - facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept { - // Set Position & Size Properties + void ImageComponentView::finalizeUpdates(RNComponentViewUpdateMask updateMask) noexcept {} - if ((layoutMetrics.displayType != m_layoutMetrics.displayType)) { - m_visual.IsVisible(layoutMetrics.displayType != facebook::react::DisplayType::None); + void ImageComponentView::OnRenderingDeviceLost() noexcept { + // Draw the text again + DrawImage(); } - updateBorderLayoutMetrics(layoutMetrics, *m_props); - - m_layoutMetrics = layoutMetrics; - - UpdateCenterPropertySet(); - m_visual.Size( - {layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor, - layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor}); - m_visual.Offset({ - layoutMetrics.frame.origin.x * layoutMetrics.pointScaleFactor, - layoutMetrics.frame.origin.y * layoutMetrics.pointScaleFactor, - 0.0f, - }); -} - -void ImageComponentView::finalizeUpdates(RNComponentViewUpdateMask updateMask) noexcept {} - -void ImageComponentView::OnRenderingDeviceLost() noexcept { - // Draw the text again - DrawImage(); -} - -void ImageComponentView::ensureDrawingSurface() noexcept { - assert(m_context.UIDispatcher().HasThreadAccess()); - - UINT width, height; - winrt::check_hresult(m_wicbmp->GetSize(&width, &height)); - - if (!m_drawingSurface && m_wicbmp) { - winrt::Windows::Foundation::Size drawingSurfaceSize{static_cast(width), static_cast(height)}; - - const auto imageProps = std::static_pointer_cast(m_props); - const auto frame{m_layoutMetrics.getContentFrame().size}; - - if (imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat) { - drawingSurfaceSize = {frame.width, frame.height}; - } else if (imageProps->blurRadius > 0) { - // The output of this effect can be larger than the input bitmap based on the blur radius and the border mode. - // If the border mode is set to D2D1_BORDER_MODE_SOFT (the default value) - // the size of the output bitmap increases by the size of the blur kernel, represented in pixels. - // This table provides an equation that you can use to compute the output bitmap. - // Output bitmap growth (X and Y) = StandardDeviation (DIPs)*6*((User DPI)/96) - const auto bmpGrowth{imageProps->blurRadius * 6 * m_layoutMetrics.pointScaleFactor}; - drawingSurfaceSize = {drawingSurfaceSize.Width + bmpGrowth, drawingSurfaceSize.Height + bmpGrowth}; - } + void ImageComponentView::ensureDrawingSurface() noexcept { + assert(m_context.UIDispatcher().HasThreadAccess()); + + UINT width, height; + winrt::check_hresult(m_wicbmp->GetSize(&width, &height)); + + if (!m_drawingSurface && m_wicbmp) { + winrt::Windows::Foundation::Size drawingSurfaceSize{ static_cast(width), static_cast(height) }; + + const auto imageProps = std::static_pointer_cast(m_props); + const auto frame{ m_layoutMetrics.getContentFrame().size }; + + if (imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat) { + drawingSurfaceSize = { frame.width, frame.height }; + } + else if (imageProps->blurRadius > 0) { + // https://learn.microsoft.com/en-us/windows/win32/direct2d/gaussian-blur#output-bitmap + // The following equation that can be used to compute the output bitmap: + // Output bitmap growth (X and Y) = (StandardDeviation(DIPs)*3 + StandardDeviation(DIPs)*3)*((User DPI)/96) + // Where StandardDeviation(DIPs)*3 is equivalent to the blur radius. + const auto bmpGrowth{ imageProps->blurRadius * 2 * m_layoutMetrics.pointScaleFactor }; + drawingSurfaceSize = { drawingSurfaceSize.Width + bmpGrowth, drawingSurfaceSize.Height + bmpGrowth }; + } - m_drawingSurface = m_compContext.CreateDrawingSurface( + m_drawingSurface = m_compContext.CreateDrawingSurface( drawingSurfaceSize, winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized, winrt::Windows::Graphics::DirectX::DirectXAlphaMode::Premultiplied); - DrawImage(); + DrawImage(); - auto surfaceBrush = m_compContext.CreateSurfaceBrush(m_drawingSurface); + auto surfaceBrush = m_compContext.CreateSurfaceBrush(m_drawingSurface); - switch (imageProps->resizeMode) { + switch (imageProps->resizeMode) { case facebook::react::ImageResizeMode::Stretch: surfaceBrush.Stretch(winrt::Microsoft::ReactNative::Composition::CompositionStretch::Fill); break; @@ -241,166 +244,173 @@ void ImageComponentView::ensureDrawingSurface() noexcept { surfaceBrush.Stretch(winrt::Microsoft::ReactNative::Composition::CompositionStretch::Uniform); break; case facebook::react::ImageResizeMode::Repeat: + // TODO - set AlignmentRatio back to 0.5f when switching between resizeModes once we no longer recreate the drawing surface on prop changes. surfaceBrush.HorizontalAlignmentRatio(0.0f); surfaceBrush.VerticalAlignmentRatio(0.0f); + // Repeat and Center use the same Stretch logic, so we can fallthrough here. [[fallthrough]]; case facebook::react::ImageResizeMode::Center: { surfaceBrush.Stretch( - (height < frame.height && width < frame.width) - ? winrt::Microsoft::ReactNative::Composition::CompositionStretch::None - : winrt::Microsoft::ReactNative::Composition::CompositionStretch::Uniform); + (height < frame.height&& width < frame.width) + ? winrt::Microsoft::ReactNative::Composition::CompositionStretch::None + : winrt::Microsoft::ReactNative::Composition::CompositionStretch::Uniform); break; } default: assert(false); - } - - m_visual.Brush(surfaceBrush); - } -} - -void ImageComponentView::DrawImage() noexcept { - // Begin our update of the surface pixels. If this is our first update, we are required - // to specify the entire surface, which nullptr is shorthand for (but, as it works out, - // any time we make an update we touch the entire surface, so we always pass nullptr). - winrt::com_ptr d2dDeviceContext; - POINT offset; - - assert(m_context.UIDispatcher().HasThreadAccess()); - - winrt::com_ptr drawingSurfaceInterop; - m_drawingSurface.as(drawingSurfaceInterop); - - if (CheckForDeviceRemoved(drawingSurfaceInterop->BeginDraw(d2dDeviceContext.put(), &offset))) { - winrt::com_ptr bitmap; - winrt::check_hresult(d2dDeviceContext->CreateBitmapFromWicBitmap(m_wicbmp.get(), nullptr, bitmap.put())); + } - d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Black, 0.0f)); - if (m_props->backgroundColor) { - d2dDeviceContext->Clear(m_props->backgroundColor.AsD2DColor()); + m_visual.Brush(surfaceBrush); } + } - const auto imageProps = std::static_pointer_cast(m_props); - - bool useEffects{ - imageProps->blurRadius > 0 || isColorMeaningful(imageProps->tintColor) || - imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat}; - - if (useEffects) { - winrt::com_ptr bitmapEffects; - winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1BitmapSource, bitmapEffects.put())); - winrt::check_hresult(bitmapEffects->SetValue(D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE, m_wicbmp.get())); + void ImageComponentView::DrawImage() noexcept { + // Begin our update of the surface pixels. If this is our first update, we are required + // to specify the entire surface, which nullptr is shorthand for (but, as it works out, + // any time we make an update we touch the entire surface, so we always pass nullptr). + winrt::com_ptr d2dDeviceContext; + POINT offset; - if (imageProps->blurRadius > 0) { - winrt::com_ptr gaussianBlurEffect; - winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1GaussianBlur, gaussianBlurEffect.put())); - winrt::check_hresult( - gaussianBlurEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, imageProps->blurRadius)); - gaussianBlurEffect->SetInputEffect(0, bitmapEffects.get()); - bitmapEffects.copy_from(gaussianBlurEffect.get()); - } + assert(m_context.UIDispatcher().HasThreadAccess()); - if (isColorMeaningful(imageProps->tintColor)) { - winrt::com_ptr tintColorEffect; - winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Flood, tintColorEffect.put())); - winrt::check_hresult(tintColorEffect->SetValue(D2D1_FLOOD_PROP_COLOR, imageProps->tintColor.AsD2DColor())); + winrt::com_ptr drawingSurfaceInterop; + m_drawingSurface.as(drawingSurfaceInterop); - winrt::com_ptr compositeEffect; - winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Composite, compositeEffect.put())); - winrt::check_hresult(compositeEffect->SetValue(D2D1_COMPOSITE_PROP_MODE, D2D1_COMPOSITE_MODE_SOURCE_IN)); - compositeEffect->SetInputEffect(0, bitmapEffects.get()); - compositeEffect->SetInputEffect(1, tintColorEffect.get()); + if (CheckForDeviceRemoved(drawingSurfaceInterop->BeginDraw(d2dDeviceContext.put(), &offset))) { + winrt::com_ptr bitmap; + winrt::check_hresult(d2dDeviceContext->CreateBitmapFromWicBitmap(m_wicbmp.get(), nullptr, bitmap.put())); - bitmapEffects.copy_from(compositeEffect.get()); + d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Black, 0.0f)); + if (m_props->backgroundColor) { + d2dDeviceContext->Clear(m_props->backgroundColor.AsD2DColor()); } - if (imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat) { - winrt::com_ptr borderEffect; - winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Border, borderEffect.put())); - winrt::check_hresult(borderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_WRAP)); - winrt::check_hresult(borderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_WRAP)); - borderEffect->SetInputEffect(0, bitmapEffects.get()); - - d2dDeviceContext->DrawImage(borderEffect.get()); - } else { - winrt::com_ptr image; - bitmapEffects->GetOutput(image.put()); - - D2D1_RECT_F imageBounds; - winrt::check_hresult(d2dDeviceContext->GetImageLocalBounds(image.get(), &imageBounds)); - - d2dDeviceContext->DrawImage( - bitmapEffects.get(), {static_cast(offset.x), static_cast(offset.y)}, imageBounds); + const auto imageProps = std::static_pointer_cast(m_props); + + bool useEffects{ + imageProps->blurRadius > 0 || isColorMeaningful(imageProps->tintColor) || + imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat }; + + if (useEffects) { + winrt::com_ptr bitmapEffects; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1BitmapSource, bitmapEffects.put())); + winrt::check_hresult(bitmapEffects->SetValue(D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE, m_wicbmp.get())); + + if (imageProps->blurRadius > 0) { + winrt::com_ptr gaussianBlurEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1GaussianBlur, gaussianBlurEffect.put())); + // https://learn.microsoft.com/en-us/windows/win32/direct2d/gaussian-blur#effect-properties + // You can compute the blur radius of the kernel by multiplying the standard deviation by 3 (radius multiplier). + constexpr float radiusMultiplier = 3; + winrt::check_hresult( + gaussianBlurEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, (imageProps->blurRadius) / radiusMultiplier)); + gaussianBlurEffect->SetInputEffect(0, bitmapEffects.get()); + bitmapEffects.copy_from(gaussianBlurEffect.get()); + } + + if (isColorMeaningful(imageProps->tintColor)) { + winrt::com_ptr tintColorEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Flood, tintColorEffect.put())); + winrt::check_hresult(tintColorEffect->SetValue(D2D1_FLOOD_PROP_COLOR, imageProps->tintColor.AsD2DColor())); + + winrt::com_ptr compositeEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Composite, compositeEffect.put())); + winrt::check_hresult(compositeEffect->SetValue(D2D1_COMPOSITE_PROP_MODE, D2D1_COMPOSITE_MODE_SOURCE_IN)); + compositeEffect->SetInputEffect(0, bitmapEffects.get()); + compositeEffect->SetInputEffect(1, tintColorEffect.get()); + + bitmapEffects.copy_from(compositeEffect.get()); + } + + if (imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat) { + winrt::com_ptr borderEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Border, borderEffect.put())); + winrt::check_hresult(borderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_WRAP)); + winrt::check_hresult(borderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_WRAP)); + borderEffect->SetInputEffect(0, bitmapEffects.get()); + + d2dDeviceContext->DrawImage(borderEffect.get()); + } + else { + winrt::com_ptr image; + bitmapEffects->GetOutput(image.put()); + + D2D1_RECT_F imageBounds; + winrt::check_hresult(d2dDeviceContext->GetImageLocalBounds(image.get(), &imageBounds)); + + d2dDeviceContext->DrawImage( + bitmapEffects.get(), { static_cast(offset.x), static_cast(offset.y) }, imageBounds); + } } - } else { - UINT width, height; - winrt::check_hresult(m_wicbmp->GetSize(&width, &height)); + else { + UINT width, height; + winrt::check_hresult(m_wicbmp->GetSize(&width, &height)); - D2D1_RECT_F rect = D2D1::RectF( + D2D1_RECT_F rect = D2D1::RectF( static_cast(offset.x / m_layoutMetrics.pointScaleFactor), static_cast(offset.y / m_layoutMetrics.pointScaleFactor), static_cast((offset.x + width) / m_layoutMetrics.pointScaleFactor), static_cast((offset.y + height) / m_layoutMetrics.pointScaleFactor)); - const auto dpi = m_layoutMetrics.pointScaleFactor * 96.0f; - float oldDpiX, oldDpiY; - d2dDeviceContext->GetDpi(&oldDpiX, &oldDpiY); - d2dDeviceContext->SetDpi(dpi, dpi); + const auto dpi = m_layoutMetrics.pointScaleFactor * 96.0f; + float oldDpiX, oldDpiY; + d2dDeviceContext->GetDpi(&oldDpiX, &oldDpiY); + d2dDeviceContext->SetDpi(dpi, dpi); - d2dDeviceContext->DrawBitmap(bitmap.get(), rect); + d2dDeviceContext->DrawBitmap(bitmap.get(), rect); - // Restore old dpi setting - d2dDeviceContext->SetDpi(oldDpiX, oldDpiY); - } + // Restore old dpi setting + d2dDeviceContext->SetDpi(oldDpiX, oldDpiY); + } - // Our update is done. EndDraw never indicates rendering device removed, so any - // failure here is unexpected and, therefore, fatal. - winrt::check_hresult(drawingSurfaceInterop->EndDraw()); + // Our update is done. EndDraw never indicates rendering device removed, so any + // failure here is unexpected and, therefore, fatal. + winrt::check_hresult(drawingSurfaceInterop->EndDraw()); + } } -} -void ImageComponentView::prepareForRecycle() noexcept {} -facebook::react::Props::Shared ImageComponentView::props() noexcept { - return m_props; -} + void ImageComponentView::prepareForRecycle() noexcept {} + facebook::react::Props::Shared ImageComponentView::props() noexcept { + return m_props; + } -facebook::react::Tag ImageComponentView::hitTest(facebook::react::Point pt, facebook::react::Point &localPt) + facebook::react::Tag ImageComponentView::hitTest(facebook::react::Point pt, facebook::react::Point& localPt) const noexcept { - facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y}; + facebook::react::Point ptLocal{ pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y }; - facebook::react::Tag targetTag; + facebook::react::Tag targetTag; - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || - m_props->pointerEvents == facebook::react::PointerEventsMode::BoxNone) && + if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + m_props->pointerEvents == facebook::react::PointerEventsMode::BoxNone) && std::any_of(m_children.rbegin(), m_children.rend(), [&targetTag, &ptLocal, &localPt](auto child) { - targetTag = static_cast(child)->hitTest(ptLocal, localPt); - return targetTag != -1; - })) - return targetTag; - - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || - m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && - ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && - ptLocal.y <= m_layoutMetrics.frame.size.height) { - localPt = ptLocal; - return tag(); + targetTag = static_cast(child)->hitTest(ptLocal, localPt); + return targetTag != -1; + })) + return targetTag; + + if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && + ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && + ptLocal.y <= m_layoutMetrics.frame.size.height) { + localPt = ptLocal; + return tag(); + } + + return -1; } - return -1; -} - -facebook::react::SharedTouchEventEmitter ImageComponentView::touchEventEmitter() noexcept { - return m_eventEmitter; -} + facebook::react::SharedTouchEventEmitter ImageComponentView::touchEventEmitter() noexcept { + return m_eventEmitter; + } -void ImageComponentView::ensureVisual() noexcept { - if (!m_visual) { - m_visual = m_compContext.CreateSpriteVisual(); + void ImageComponentView::ensureVisual() noexcept { + if (!m_visual) { + m_visual = m_compContext.CreateSpriteVisual(); + } } -} -winrt::Microsoft::ReactNative::Composition::IVisual ImageComponentView::Visual() const noexcept { - return m_visual; -} + winrt::Microsoft::ReactNative::Composition::IVisual ImageComponentView::Visual() const noexcept { + return m_visual; + } } // namespace Microsoft::ReactNative From b719c0c5d825e20979247cdb738473d8b0901038 Mon Sep 17 00:00:00 2001 From: Marlene Cota Date: Fri, 20 Jan 2023 15:36:23 -0800 Subject: [PATCH 5/5] formatting --- .../Fabric/Composition/ImageComponentView.cpp | 569 +++++++++--------- 1 file changed, 283 insertions(+), 286 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp index 96929be3ba5..4c2fd479639 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp @@ -22,218 +22,216 @@ #include #include "CompositionHelpers.h" -extern "C" HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT SDKVersion, IWICImagingFactory * *ppIWICImagingFactory); +extern "C" HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT SDKVersion, IWICImagingFactory **ppIWICImagingFactory); namespace Microsoft::ReactNative { - ImageComponentView::WindowsImageResponseObserver::WindowsImageResponseObserver( +ImageComponentView::WindowsImageResponseObserver::WindowsImageResponseObserver( std::shared_ptr image) { - m_image = image; - } + m_image = image; +} - void ImageComponentView::WindowsImageResponseObserver::didReceiveProgress(float progress) const { - // TODO progress? - } +void ImageComponentView::WindowsImageResponseObserver::didReceiveProgress(float progress) const { + // TODO progress? +} - void ImageComponentView::WindowsImageResponseObserver::didReceiveImage( - facebook::react::ImageResponse const& imageResponse) const { - auto sharedwicbmp = std::static_pointer_cast>(imageResponse.getImage()); - m_image->m_context.UIDispatcher().Post( +void ImageComponentView::WindowsImageResponseObserver::didReceiveImage( + facebook::react::ImageResponse const &imageResponse) const { + auto sharedwicbmp = std::static_pointer_cast>(imageResponse.getImage()); + m_image->m_context.UIDispatcher().Post( [wicbmp = *sharedwicbmp, image = m_image]() { image->didReceiveImage(wicbmp); }); - } +} - void ImageComponentView::WindowsImageResponseObserver::didReceiveFailure() const { - m_image->didReceiveFailureFromObserver(); - } +void ImageComponentView::WindowsImageResponseObserver::didReceiveFailure() const { + m_image->didReceiveFailureFromObserver(); +} - ImageComponentView::ImageComponentView( - const winrt::Microsoft::ReactNative::Composition::ICompositionContext& compContext, +ImageComponentView::ImageComponentView( + const winrt::Microsoft::ReactNative::Composition::ICompositionContext &compContext, facebook::react::Tag tag, - winrt::Microsoft::ReactNative::ReactContext const& reactContext) + winrt::Microsoft::ReactNative::ReactContext const &reactContext) : Super(compContext, tag), m_context(reactContext) { - static auto const defaultProps = std::make_shared(); - m_props = defaultProps; + static auto const defaultProps = std::make_shared(); + m_props = defaultProps; +} + +std::vector +ImageComponentView::supplementalComponentDescriptorProviders() noexcept { + return {}; +} + +void ImageComponentView::mountChildComponentView(const IComponentView &childComponentView, uint32_t index) noexcept { + assert(false); +} + +void ImageComponentView::unmountChildComponentView(const IComponentView &childComponentView, uint32_t index) noexcept { + assert(false); +} + +void ImageComponentView::ImageLoadStart() noexcept { + auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); + if (imageEventEmitter) { + imageEventEmitter->onLoadStart(); } +} - std::vector - ImageComponentView::supplementalComponentDescriptorProviders() noexcept { - return {}; - } +void ImageComponentView::didReceiveImage(const winrt::com_ptr &wicbmp) noexcept { + // TODO check for recycled? - void ImageComponentView::mountChildComponentView(const IComponentView& childComponentView, uint32_t index) noexcept { - assert(false); + auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); + if (imageEventEmitter) { + imageEventEmitter->onLoad(); + imageEventEmitter->onLoadEnd(); } - void ImageComponentView::unmountChildComponentView(const IComponentView& childComponentView, uint32_t index) noexcept { - assert(false); - } - - void ImageComponentView::ImageLoadStart() noexcept { - auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); - if (imageEventEmitter) { - imageEventEmitter->onLoadStart(); - } - } - - void ImageComponentView::didReceiveImage(const winrt::com_ptr& wicbmp) noexcept { - // TODO check for recycled? - - auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); - if (imageEventEmitter) { - imageEventEmitter->onLoad(); - imageEventEmitter->onLoadEnd(); - } - - // TODO - handle imageProps.capInsets + // TODO - handle imageProps.capInsets #ifdef DEBUG - auto uiDispatcher = m_context.UIDispatcher(); - assert(uiDispatcher.HasThreadAccess()); + auto uiDispatcher = m_context.UIDispatcher(); + assert(uiDispatcher.HasThreadAccess()); #endif - m_wicbmp = wicbmp; - ensureDrawingSurface(); - } + m_wicbmp = wicbmp; + ensureDrawingSurface(); +} - void ImageComponentView::didReceiveFailureFromObserver() noexcept { - auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); - if (imageEventEmitter) { - imageEventEmitter->onError(); - imageEventEmitter->onLoadEnd(); - } +void ImageComponentView::didReceiveFailureFromObserver() noexcept { + auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter); + if (imageEventEmitter) { + imageEventEmitter->onError(); + imageEventEmitter->onLoadEnd(); } +} - void ImageComponentView::updateProps( - facebook::react::Props::Shared const& props, - facebook::react::Props::Shared const& oldProps) noexcept { - const auto &oldImageProps = *std::static_pointer_cast(m_props); - const auto &newImageProps = *std::static_pointer_cast(props); +void ImageComponentView::updateProps( + facebook::react::Props::Shared const &props, + facebook::react::Props::Shared const &oldProps) noexcept { + const auto &oldImageProps = *std::static_pointer_cast(m_props); + const auto &newImageProps = *std::static_pointer_cast(props); - ensureVisual(); + ensureVisual(); - updateBorderProps(oldImageProps, newImageProps); + updateBorderProps(oldImageProps, newImageProps); - if (oldImageProps.backgroundColor != newImageProps.backgroundColor - || oldImageProps.blurRadius != newImageProps.blurRadius - || oldImageProps.tintColor != newImageProps.tintColor - || oldImageProps.resizeMode != newImageProps.resizeMode) { - m_drawingSurface = nullptr; // TODO dont need to recreate the surface just to redraw... - } - - if (oldImageProps.opacity != newImageProps.opacity) { - m_visual.Opacity(newImageProps.opacity); - } + if (oldImageProps.backgroundColor != newImageProps.backgroundColor || + oldImageProps.blurRadius != newImageProps.blurRadius || oldImageProps.tintColor != newImageProps.tintColor || + oldImageProps.resizeMode != newImageProps.resizeMode) { + m_drawingSurface = nullptr; // TODO dont need to recreate the surface just to redraw... + } - m_props = std::static_pointer_cast(props); + if (oldImageProps.opacity != newImageProps.opacity) { + m_visual.Opacity(newImageProps.opacity); } - void ImageComponentView::updateState( - facebook::react::State::Shared const& state, - facebook::react::State::Shared const& oldState) noexcept { - auto oldImageState = std::static_pointer_cast(m_state); - auto newImageState = std::static_pointer_cast(state); + m_props = std::static_pointer_cast(props); +} - if (!m_imageResponseObserver) { - // Should ViewComponents enable_shared_from_this? then we dont need this dance to get a shared_ptr - std::shared_ptr fabricuiManager = +void ImageComponentView::updateState( + facebook::react::State::Shared const &state, + facebook::react::State::Shared const &oldState) noexcept { + auto oldImageState = std::static_pointer_cast(m_state); + auto newImageState = std::static_pointer_cast(state); + + if (!m_imageResponseObserver) { + // Should ViewComponents enable_shared_from_this? then we dont need this dance to get a shared_ptr + std::shared_ptr fabricuiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties()); - auto componentViewDescriptor = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(m_tag); + auto componentViewDescriptor = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(m_tag); - m_imageResponseObserver = std::make_shared( + m_imageResponseObserver = std::make_shared( std::static_pointer_cast(componentViewDescriptor.view)); - } + } - setStateAndResubscribeImageResponseObserver(newImageState); - bool havePreviousData = oldImageState && oldImageState->getData().getImageSource() != facebook::react::ImageSource{}; + setStateAndResubscribeImageResponseObserver(newImageState); + bool havePreviousData = oldImageState && oldImageState->getData().getImageSource() != facebook::react::ImageSource{}; - if (!havePreviousData || + if (!havePreviousData || (newImageState && newImageState->getData().getImageSource() != oldImageState->getData().getImageSource())) { - // Loading actually starts a little before this, but this is the first time we know - // the image is loading and can fire an event from this component - std::static_pointer_cast(m_eventEmitter)->onLoadStart(); - } + // Loading actually starts a little before this, but this is the first time we know + // the image is loading and can fire an event from this component + std::static_pointer_cast(m_eventEmitter)->onLoadStart(); } +} - void ImageComponentView::setStateAndResubscribeImageResponseObserver( - facebook::react::ImageShadowNode::ConcreteState::Shared const& state) noexcept { - if (m_state) { - auto& observerCoordinator = m_state->getData().getImageRequest().getObserverCoordinator(); - observerCoordinator.removeObserver(*m_imageResponseObserver); - } - - m_state = state; - - if (m_state) { - auto& observerCoordinator = m_state->getData().getImageRequest().getObserverCoordinator(); - observerCoordinator.addObserver(*m_imageResponseObserver); - } +void ImageComponentView::setStateAndResubscribeImageResponseObserver( + facebook::react::ImageShadowNode::ConcreteState::Shared const &state) noexcept { + if (m_state) { + auto &observerCoordinator = m_state->getData().getImageRequest().getObserverCoordinator(); + observerCoordinator.removeObserver(*m_imageResponseObserver); } - void ImageComponentView::updateLayoutMetrics( - facebook::react::LayoutMetrics const& layoutMetrics, - facebook::react::LayoutMetrics const& oldLayoutMetrics) noexcept { - // Set Position & Size Properties - - if ((layoutMetrics.displayType != m_layoutMetrics.displayType)) { - m_visual.IsVisible(layoutMetrics.displayType != facebook::react::DisplayType::None); - } - - updateBorderLayoutMetrics(layoutMetrics, *m_props); + m_state = state; - m_layoutMetrics = layoutMetrics; - - UpdateCenterPropertySet(); - m_visual.Size( - { layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor, - layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor }); - m_visual.Offset({ - layoutMetrics.frame.origin.x * layoutMetrics.pointScaleFactor, - layoutMetrics.frame.origin.y * layoutMetrics.pointScaleFactor, - 0.0f, - }); + if (m_state) { + auto &observerCoordinator = m_state->getData().getImageRequest().getObserverCoordinator(); + observerCoordinator.addObserver(*m_imageResponseObserver); } +} - void ImageComponentView::finalizeUpdates(RNComponentViewUpdateMask updateMask) noexcept {} +void ImageComponentView::updateLayoutMetrics( + facebook::react::LayoutMetrics const &layoutMetrics, + facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept { + // Set Position & Size Properties - void ImageComponentView::OnRenderingDeviceLost() noexcept { - // Draw the text again - DrawImage(); + if ((layoutMetrics.displayType != m_layoutMetrics.displayType)) { + m_visual.IsVisible(layoutMetrics.displayType != facebook::react::DisplayType::None); } - void ImageComponentView::ensureDrawingSurface() noexcept { - assert(m_context.UIDispatcher().HasThreadAccess()); - - UINT width, height; - winrt::check_hresult(m_wicbmp->GetSize(&width, &height)); - - if (!m_drawingSurface && m_wicbmp) { - winrt::Windows::Foundation::Size drawingSurfaceSize{ static_cast(width), static_cast(height) }; - - const auto imageProps = std::static_pointer_cast(m_props); - const auto frame{ m_layoutMetrics.getContentFrame().size }; - - if (imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat) { - drawingSurfaceSize = { frame.width, frame.height }; - } - else if (imageProps->blurRadius > 0) { - // https://learn.microsoft.com/en-us/windows/win32/direct2d/gaussian-blur#output-bitmap - // The following equation that can be used to compute the output bitmap: - // Output bitmap growth (X and Y) = (StandardDeviation(DIPs)*3 + StandardDeviation(DIPs)*3)*((User DPI)/96) - // Where StandardDeviation(DIPs)*3 is equivalent to the blur radius. - const auto bmpGrowth{ imageProps->blurRadius * 2 * m_layoutMetrics.pointScaleFactor }; - drawingSurfaceSize = { drawingSurfaceSize.Width + bmpGrowth, drawingSurfaceSize.Height + bmpGrowth }; - } + updateBorderLayoutMetrics(layoutMetrics, *m_props); + + m_layoutMetrics = layoutMetrics; + + UpdateCenterPropertySet(); + m_visual.Size( + {layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor, + layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor}); + m_visual.Offset({ + layoutMetrics.frame.origin.x * layoutMetrics.pointScaleFactor, + layoutMetrics.frame.origin.y * layoutMetrics.pointScaleFactor, + 0.0f, + }); +} + +void ImageComponentView::finalizeUpdates(RNComponentViewUpdateMask updateMask) noexcept {} + +void ImageComponentView::OnRenderingDeviceLost() noexcept { + // Draw the text again + DrawImage(); +} + +void ImageComponentView::ensureDrawingSurface() noexcept { + assert(m_context.UIDispatcher().HasThreadAccess()); + + UINT width, height; + winrt::check_hresult(m_wicbmp->GetSize(&width, &height)); + + if (!m_drawingSurface && m_wicbmp) { + winrt::Windows::Foundation::Size drawingSurfaceSize{static_cast(width), static_cast(height)}; + + const auto imageProps = std::static_pointer_cast(m_props); + const auto frame{m_layoutMetrics.getContentFrame().size}; + + if (imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat) { + drawingSurfaceSize = {frame.width, frame.height}; + } else if (imageProps->blurRadius > 0) { + // https://learn.microsoft.com/en-us/windows/win32/direct2d/gaussian-blur#output-bitmap + // The following equation that can be used to compute the output bitmap: + // Output bitmap growth (X and Y) = (StandardDeviation(DIPs)*3 + StandardDeviation(DIPs)*3)*((User DPI)/96) + // Where StandardDeviation(DIPs)*3 is equivalent to the blur radius. + const auto bmpGrowth{imageProps->blurRadius * 2 * m_layoutMetrics.pointScaleFactor}; + drawingSurfaceSize = {drawingSurfaceSize.Width + bmpGrowth, drawingSurfaceSize.Height + bmpGrowth}; + } - m_drawingSurface = m_compContext.CreateDrawingSurface( + m_drawingSurface = m_compContext.CreateDrawingSurface( drawingSurfaceSize, winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized, winrt::Windows::Graphics::DirectX::DirectXAlphaMode::Premultiplied); - DrawImage(); + DrawImage(); - auto surfaceBrush = m_compContext.CreateSurfaceBrush(m_drawingSurface); + auto surfaceBrush = m_compContext.CreateSurfaceBrush(m_drawingSurface); - switch (imageProps->resizeMode) { + switch (imageProps->resizeMode) { case facebook::react::ImageResizeMode::Stretch: surfaceBrush.Stretch(winrt::Microsoft::ReactNative::Composition::CompositionStretch::Fill); break; @@ -244,173 +242,172 @@ namespace Microsoft::ReactNative { surfaceBrush.Stretch(winrt::Microsoft::ReactNative::Composition::CompositionStretch::Uniform); break; case facebook::react::ImageResizeMode::Repeat: - // TODO - set AlignmentRatio back to 0.5f when switching between resizeModes once we no longer recreate the drawing surface on prop changes. + // TODO - set AlignmentRatio back to 0.5f when switching between resizeModes once we no longer recreate the + // drawing surface on prop changes. surfaceBrush.HorizontalAlignmentRatio(0.0f); surfaceBrush.VerticalAlignmentRatio(0.0f); // Repeat and Center use the same Stretch logic, so we can fallthrough here. [[fallthrough]]; case facebook::react::ImageResizeMode::Center: { surfaceBrush.Stretch( - (height < frame.height&& width < frame.width) - ? winrt::Microsoft::ReactNative::Composition::CompositionStretch::None - : winrt::Microsoft::ReactNative::Composition::CompositionStretch::Uniform); + (height < frame.height && width < frame.width) + ? winrt::Microsoft::ReactNative::Composition::CompositionStretch::None + : winrt::Microsoft::ReactNative::Composition::CompositionStretch::Uniform); break; } default: assert(false); - } - - m_visual.Brush(surfaceBrush); } + + m_visual.Brush(surfaceBrush); } +} - void ImageComponentView::DrawImage() noexcept { - // Begin our update of the surface pixels. If this is our first update, we are required - // to specify the entire surface, which nullptr is shorthand for (but, as it works out, - // any time we make an update we touch the entire surface, so we always pass nullptr). - winrt::com_ptr d2dDeviceContext; - POINT offset; +void ImageComponentView::DrawImage() noexcept { + // Begin our update of the surface pixels. If this is our first update, we are required + // to specify the entire surface, which nullptr is shorthand for (but, as it works out, + // any time we make an update we touch the entire surface, so we always pass nullptr). + winrt::com_ptr d2dDeviceContext; + POINT offset; - assert(m_context.UIDispatcher().HasThreadAccess()); + assert(m_context.UIDispatcher().HasThreadAccess()); - winrt::com_ptr drawingSurfaceInterop; - m_drawingSurface.as(drawingSurfaceInterop); + winrt::com_ptr drawingSurfaceInterop; + m_drawingSurface.as(drawingSurfaceInterop); - if (CheckForDeviceRemoved(drawingSurfaceInterop->BeginDraw(d2dDeviceContext.put(), &offset))) { - winrt::com_ptr bitmap; - winrt::check_hresult(d2dDeviceContext->CreateBitmapFromWicBitmap(m_wicbmp.get(), nullptr, bitmap.put())); + if (CheckForDeviceRemoved(drawingSurfaceInterop->BeginDraw(d2dDeviceContext.put(), &offset))) { + winrt::com_ptr bitmap; + winrt::check_hresult(d2dDeviceContext->CreateBitmapFromWicBitmap(m_wicbmp.get(), nullptr, bitmap.put())); - d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Black, 0.0f)); - if (m_props->backgroundColor) { - d2dDeviceContext->Clear(m_props->backgroundColor.AsD2DColor()); + d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Black, 0.0f)); + if (m_props->backgroundColor) { + d2dDeviceContext->Clear(m_props->backgroundColor.AsD2DColor()); + } + + const auto imageProps = std::static_pointer_cast(m_props); + + bool useEffects{ + imageProps->blurRadius > 0 || isColorMeaningful(imageProps->tintColor) || + imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat}; + + if (useEffects) { + winrt::com_ptr bitmapEffects; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1BitmapSource, bitmapEffects.put())); + winrt::check_hresult(bitmapEffects->SetValue(D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE, m_wicbmp.get())); + + if (imageProps->blurRadius > 0) { + winrt::com_ptr gaussianBlurEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1GaussianBlur, gaussianBlurEffect.put())); + // https://learn.microsoft.com/en-us/windows/win32/direct2d/gaussian-blur#effect-properties + // You can compute the blur radius of the kernel by multiplying the standard deviation by 3 (radius multiplier). + constexpr float radiusMultiplier = 3; + winrt::check_hresult(gaussianBlurEffect->SetValue( + D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, (imageProps->blurRadius) / radiusMultiplier)); + gaussianBlurEffect->SetInputEffect(0, bitmapEffects.get()); + bitmapEffects.copy_from(gaussianBlurEffect.get()); } - const auto imageProps = std::static_pointer_cast(m_props); - - bool useEffects{ - imageProps->blurRadius > 0 || isColorMeaningful(imageProps->tintColor) || - imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat }; - - if (useEffects) { - winrt::com_ptr bitmapEffects; - winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1BitmapSource, bitmapEffects.put())); - winrt::check_hresult(bitmapEffects->SetValue(D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE, m_wicbmp.get())); - - if (imageProps->blurRadius > 0) { - winrt::com_ptr gaussianBlurEffect; - winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1GaussianBlur, gaussianBlurEffect.put())); - // https://learn.microsoft.com/en-us/windows/win32/direct2d/gaussian-blur#effect-properties - // You can compute the blur radius of the kernel by multiplying the standard deviation by 3 (radius multiplier). - constexpr float radiusMultiplier = 3; - winrt::check_hresult( - gaussianBlurEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, (imageProps->blurRadius) / radiusMultiplier)); - gaussianBlurEffect->SetInputEffect(0, bitmapEffects.get()); - bitmapEffects.copy_from(gaussianBlurEffect.get()); - } - - if (isColorMeaningful(imageProps->tintColor)) { - winrt::com_ptr tintColorEffect; - winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Flood, tintColorEffect.put())); - winrt::check_hresult(tintColorEffect->SetValue(D2D1_FLOOD_PROP_COLOR, imageProps->tintColor.AsD2DColor())); - - winrt::com_ptr compositeEffect; - winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Composite, compositeEffect.put())); - winrt::check_hresult(compositeEffect->SetValue(D2D1_COMPOSITE_PROP_MODE, D2D1_COMPOSITE_MODE_SOURCE_IN)); - compositeEffect->SetInputEffect(0, bitmapEffects.get()); - compositeEffect->SetInputEffect(1, tintColorEffect.get()); - - bitmapEffects.copy_from(compositeEffect.get()); - } - - if (imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat) { - winrt::com_ptr borderEffect; - winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Border, borderEffect.put())); - winrt::check_hresult(borderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_WRAP)); - winrt::check_hresult(borderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_WRAP)); - borderEffect->SetInputEffect(0, bitmapEffects.get()); - - d2dDeviceContext->DrawImage(borderEffect.get()); - } - else { - winrt::com_ptr image; - bitmapEffects->GetOutput(image.put()); - - D2D1_RECT_F imageBounds; - winrt::check_hresult(d2dDeviceContext->GetImageLocalBounds(image.get(), &imageBounds)); - - d2dDeviceContext->DrawImage( - bitmapEffects.get(), { static_cast(offset.x), static_cast(offset.y) }, imageBounds); - } + if (isColorMeaningful(imageProps->tintColor)) { + winrt::com_ptr tintColorEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Flood, tintColorEffect.put())); + winrt::check_hresult(tintColorEffect->SetValue(D2D1_FLOOD_PROP_COLOR, imageProps->tintColor.AsD2DColor())); + + winrt::com_ptr compositeEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Composite, compositeEffect.put())); + winrt::check_hresult(compositeEffect->SetValue(D2D1_COMPOSITE_PROP_MODE, D2D1_COMPOSITE_MODE_SOURCE_IN)); + compositeEffect->SetInputEffect(0, bitmapEffects.get()); + compositeEffect->SetInputEffect(1, tintColorEffect.get()); + + bitmapEffects.copy_from(compositeEffect.get()); } - else { - UINT width, height; - winrt::check_hresult(m_wicbmp->GetSize(&width, &height)); - D2D1_RECT_F rect = D2D1::RectF( + if (imageProps->resizeMode == facebook::react::ImageResizeMode::Repeat) { + winrt::com_ptr borderEffect; + winrt::check_hresult(d2dDeviceContext->CreateEffect(CLSID_D2D1Border, borderEffect.put())); + winrt::check_hresult(borderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_WRAP)); + winrt::check_hresult(borderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_WRAP)); + borderEffect->SetInputEffect(0, bitmapEffects.get()); + + d2dDeviceContext->DrawImage(borderEffect.get()); + } else { + winrt::com_ptr image; + bitmapEffects->GetOutput(image.put()); + + D2D1_RECT_F imageBounds; + winrt::check_hresult(d2dDeviceContext->GetImageLocalBounds(image.get(), &imageBounds)); + + d2dDeviceContext->DrawImage( + bitmapEffects.get(), {static_cast(offset.x), static_cast(offset.y)}, imageBounds); + } + } else { + UINT width, height; + winrt::check_hresult(m_wicbmp->GetSize(&width, &height)); + + D2D1_RECT_F rect = D2D1::RectF( static_cast(offset.x / m_layoutMetrics.pointScaleFactor), static_cast(offset.y / m_layoutMetrics.pointScaleFactor), static_cast((offset.x + width) / m_layoutMetrics.pointScaleFactor), static_cast((offset.y + height) / m_layoutMetrics.pointScaleFactor)); - const auto dpi = m_layoutMetrics.pointScaleFactor * 96.0f; - float oldDpiX, oldDpiY; - d2dDeviceContext->GetDpi(&oldDpiX, &oldDpiY); - d2dDeviceContext->SetDpi(dpi, dpi); - - d2dDeviceContext->DrawBitmap(bitmap.get(), rect); + const auto dpi = m_layoutMetrics.pointScaleFactor * 96.0f; + float oldDpiX, oldDpiY; + d2dDeviceContext->GetDpi(&oldDpiX, &oldDpiY); + d2dDeviceContext->SetDpi(dpi, dpi); - // Restore old dpi setting - d2dDeviceContext->SetDpi(oldDpiX, oldDpiY); - } + d2dDeviceContext->DrawBitmap(bitmap.get(), rect); - // Our update is done. EndDraw never indicates rendering device removed, so any - // failure here is unexpected and, therefore, fatal. - winrt::check_hresult(drawingSurfaceInterop->EndDraw()); + // Restore old dpi setting + d2dDeviceContext->SetDpi(oldDpiX, oldDpiY); } - } - void ImageComponentView::prepareForRecycle() noexcept {} - facebook::react::Props::Shared ImageComponentView::props() noexcept { - return m_props; + // Our update is done. EndDraw never indicates rendering device removed, so any + // failure here is unexpected and, therefore, fatal. + winrt::check_hresult(drawingSurfaceInterop->EndDraw()); } +} - facebook::react::Tag ImageComponentView::hitTest(facebook::react::Point pt, facebook::react::Point& localPt) +void ImageComponentView::prepareForRecycle() noexcept {} +facebook::react::Props::Shared ImageComponentView::props() noexcept { + return m_props; +} + +facebook::react::Tag ImageComponentView::hitTest(facebook::react::Point pt, facebook::react::Point &localPt) const noexcept { - facebook::react::Point ptLocal{ pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y }; + facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y}; - facebook::react::Tag targetTag; + facebook::react::Tag targetTag; - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || - m_props->pointerEvents == facebook::react::PointerEventsMode::BoxNone) && + if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + m_props->pointerEvents == facebook::react::PointerEventsMode::BoxNone) && std::any_of(m_children.rbegin(), m_children.rend(), [&targetTag, &ptLocal, &localPt](auto child) { - targetTag = static_cast(child)->hitTest(ptLocal, localPt); - return targetTag != -1; - })) - return targetTag; - - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || - m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && - ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && - ptLocal.y <= m_layoutMetrics.frame.size.height) { - localPt = ptLocal; - return tag(); - } - - return -1; + targetTag = static_cast(child)->hitTest(ptLocal, localPt); + return targetTag != -1; + })) + return targetTag; + + if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && + ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && + ptLocal.y <= m_layoutMetrics.frame.size.height) { + localPt = ptLocal; + return tag(); } - facebook::react::SharedTouchEventEmitter ImageComponentView::touchEventEmitter() noexcept { - return m_eventEmitter; - } + return -1; +} - void ImageComponentView::ensureVisual() noexcept { - if (!m_visual) { - m_visual = m_compContext.CreateSpriteVisual(); - } - } +facebook::react::SharedTouchEventEmitter ImageComponentView::touchEventEmitter() noexcept { + return m_eventEmitter; +} - winrt::Microsoft::ReactNative::Composition::IVisual ImageComponentView::Visual() const noexcept { - return m_visual; +void ImageComponentView::ensureVisual() noexcept { + if (!m_visual) { + m_visual = m_compContext.CreateSpriteVisual(); } +} + +winrt::Microsoft::ReactNative::Composition::IVisual ImageComponentView::Visual() const noexcept { + return m_visual; +} } // namespace Microsoft::ReactNative