in vnext/Microsoft.ReactNative/Views/Image/ReactImage.cpp [212:409]
winrt::fire_and_forget ReactImage::SetBackground(bool fireLoadEndEvent) {
const ReactImageSource source{m_imageSource};
winrt::Uri uri{UriTryCreate(Utf8ToUtf16(source.uri))};
// Increment the image source ID before any co_await calls
auto currentImageSourceId = ++m_imageSourceId;
const bool fromStream{
source.sourceType == ImageSourceType::Download || source.sourceType == ImageSourceType::InlineData};
winrt::InMemoryRandomAccessStream memoryStream{nullptr};
// get weak reference before any co_await calls
auto weak_this{get_weak()};
try {
memoryStream = co_await GetImageMemoryStreamAsync(source);
// Fire failed load event if we're not loading from URI and the memory stream is null.
if (fromStream && !memoryStream) {
if (auto strong_this{weak_this.get()}) {
strong_this->m_onLoadEndEvent(*strong_this, false);
}
co_return;
}
} catch (winrt::hresult_error const &) {
const auto strong_this{weak_this.get()};
if (strong_this && fireLoadEndEvent) {
strong_this->m_onLoadEndEvent(*strong_this, false);
}
co_return;
}
if (auto strong_this{weak_this.get()}) {
// If the image source has been updated since this operation started, do not continue
if (currentImageSourceId != strong_this->m_imageSourceId) {
co_return;
}
if (strong_this->m_useCompositionBrush) {
const auto compositionBrush{ReactImageBrush::Create()};
compositionBrush->BlurRadius(strong_this->m_blurRadius);
compositionBrush->TintColor(strong_this->m_tintColor);
const auto surface = fromStream ? winrt::LoadedImageSurface::StartLoadFromStream(memoryStream)
: uri ? winrt::LoadedImageSurface::StartLoadFromUri(uri) : nullptr;
m_sizeChangedRevoker = strong_this->SizeChanged(
winrt::auto_revoke, [compositionBrush](const auto &, const winrt::SizeChangedEventArgs &args) {
compositionBrush->AvailableSize(args.NewSize());
});
if (strong_this->m_surfaceLoadedRevoker) {
strong_this->m_surfaceLoadedRevoker.revoke();
}
if (surface) {
strong_this->m_surfaceLoadedRevoker = surface.LoadCompleted(
winrt::auto_revoke,
[weak_this, compositionBrush, surface, fireLoadEndEvent, uri](
winrt::LoadedImageSurface const & /*sender*/,
winrt::LoadedImageSourceLoadCompletedEventArgs const &args) {
if (auto strong_this{weak_this.get()}) {
bool succeeded{false};
if (args.Status() == winrt::LoadedImageSourceLoadStatus::Success) {
winrt::Size size{surface.DecodedPhysicalSize()};
strong_this->m_imageSource.height = size.Height;
strong_this->m_imageSource.width = size.Width;
// If we are dynamically switching the resizeMode to 'repeat', then
// the SizeChanged event has already fired and the ReactImageBrush's
// size has not been set. Use ActualWidth/Height in that case.
if (compositionBrush->AvailableSize() == winrt::Size{0, 0}) {
compositionBrush->AvailableSize(
{static_cast<float>(strong_this->ActualWidth()),
static_cast<float>(strong_this->ActualHeight())});
}
compositionBrush->Source(surface);
compositionBrush->ResizeMode(strong_this->m_resizeMode);
compositionBrush->BlurRadius(strong_this->m_blurRadius);
compositionBrush->TintColor(strong_this->m_tintColor);
strong_this->Background(compositionBrush.as<winrt::XamlCompositionBrushBase>());
succeeded = true;
} else {
ImageFailed(uri, args);
}
if (fireLoadEndEvent) {
strong_this->m_onLoadEndEvent(*strong_this, succeeded);
}
strong_this->m_sizeChangedRevoker.revoke();
}
});
}
} else {
winrt::ImageBrush imageBrush{strong_this->Background().try_as<winrt::ImageBrush>()};
bool createImageBrush{!imageBrush};
if (createImageBrush) {
imageBrush = winrt::ImageBrush{};
imageBrush.Stretch(strong_this->ResizeModeToStretch());
}
if (source.sourceFormat == ImageSourceFormat::Svg) {
winrt::SvgImageSource svgImageSource{imageBrush.ImageSource().try_as<winrt::SvgImageSource>()};
if (!svgImageSource) {
svgImageSource = winrt::SvgImageSource{};
strong_this->m_svgImageSourceOpenedRevoker =
svgImageSource.Opened(winrt::auto_revoke, [weak_this, fireLoadEndEvent](const auto &, const auto &) {
auto strong_this{weak_this.get()};
if (strong_this && fireLoadEndEvent) {
strong_this->m_onLoadEndEvent(*strong_this, true);
}
});
strong_this->m_svgImageSourceOpenFailedRevoker = svgImageSource.OpenFailed(
winrt::auto_revoke, [weak_this, fireLoadEndEvent, svgImageSource](const auto &, const auto &args) {
auto strong_this{weak_this.get()};
if (strong_this && fireLoadEndEvent) {
strong_this->m_onLoadEndEvent(*strong_this, false);
}
ImageFailed(svgImageSource, args);
});
imageBrush.ImageSource(svgImageSource);
}
if (fromStream) {
co_await svgImageSource.SetSourceAsync(memoryStream);
} else {
svgImageSource.UriSource(uri);
}
} else {
winrt::BitmapImage bitmapImage{imageBrush.ImageSource().try_as<winrt::BitmapImage>()};
if (!bitmapImage) {
bitmapImage = winrt::BitmapImage{};
strong_this->m_bitmapImageOpened = bitmapImage.ImageOpened(
winrt::auto_revoke, [imageBrush, weak_this, fireLoadEndEvent](const auto &, const auto &) {
imageBrush.Opacity(1);
auto strong_this{weak_this.get()};
if (strong_this && fireLoadEndEvent) {
if (auto bitmap{imageBrush.ImageSource().try_as<winrt::BitmapImage>()}) {
strong_this->m_imageSource.height = bitmap.PixelHeight();
strong_this->m_imageSource.width = bitmap.PixelWidth();
imageBrush.Stretch(strong_this->ResizeModeToStretch());
}
strong_this->m_onLoadEndEvent(*strong_this, true);
}
});
strong_this->m_bitmapImageFailed = bitmapImage.ImageFailed(
winrt::auto_revoke,
[imageBrush, weak_this, fireLoadEndEvent, bitmapImage](const auto &, const auto &args) {
imageBrush.Opacity(1);
auto strong_this{weak_this.get()};
if (strong_this && fireLoadEndEvent) {
strong_this->m_onLoadEndEvent(*strong_this, false);
}
ImageFailed(bitmapImage, args);
});
imageBrush.ImageSource(bitmapImage);
}
if (fromStream) {
co_await bitmapImage.SetSourceAsync(memoryStream);
} else {
bitmapImage.UriSource(uri);
// It is possible that the same URI will be set twice if an intermediate update occurs that requires
// asynchronous behavior (e.g., when the ImageSourceType is ::Download or ::InlineData). In this case,
// do not set the opacity to zero as the ImageOpened event will not fire.
auto currentUri = bitmapImage.UriSource();
if (uri && currentUri && uri.AbsoluteUri() != currentUri.AbsoluteUri()) {
// TODO: When we change the source of a BitmapImage, we're getting a flicker of the old image
// being resized to the size of the new image. This is a temporary workaround.
imageBrush.Opacity(0);
}
}
}
if (createImageBrush) {
strong_this->Background(imageBrush);
}
}
}
}