diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index 611e26582d..a557b7e3b3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -121,9 +121,9 @@ namespace AzToolsFramework { return 0; } - bool thumbnailLoading; - ThumbnailerRequestsBus::BroadcastResult(thumbnailLoading, &ThumbnailerRequests::IsLoading, thumbnailKey, m_thumbnailContext.c_str()); - if (thumbnailLoading) + + const Thumbnail::State thumbnailState = thumbnail->GetState(); + if (thumbnailState == Thumbnail::State::Loading) { AzQtComponents::StyledBusyLabel* busyLabel; AssetBrowserComponentRequestBus::BroadcastResult(busyLabel , &AssetBrowserComponentRequests::GetStyledBusyLabel); @@ -132,7 +132,7 @@ namespace AzToolsFramework busyLabel->DrawTo(painter, QRectF(point.x(), point.y(), size.width(), size.height())); } } - else + else if (thumbnailState == Thumbnail::State::Ready) { // Scaling and centering pixmap within bounds to preserve aspect ratio const QPixmap pixmap = thumbnail->GetPixmap().scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); @@ -140,6 +140,10 @@ namespace AzToolsFramework const QPoint pointDelta = QPoint(sizeDelta.width() / 2, sizeDelta.height() / 2); painter->drawPixmap(point + pointDelta, pixmap); } + else + { + AZ_Assert(false, "Thumbnail state %d unexpected here", int(thumbnailState)); + } return m_iconSize; } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Previewer/ImagePreviewer.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Previewer/ImagePreviewer.cpp index fb3ecbf6df..a0e33e28cd 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Previewer/ImagePreviewer.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Previewer/ImagePreviewer.cpp @@ -46,6 +46,12 @@ namespace ImageProcessingAtom ImagePreviewer::~ImagePreviewer() { + AZ::SystemTickBus::Handler::BusDisconnect(); + + if (m_createDisplayTextureResult.isRunning()) + { + m_createDisplayTextureResult.waitForFinished(); + } } void ImagePreviewer::Clear() const @@ -209,21 +215,30 @@ namespace ImageProcessingAtom m_ui->m_fileInfoCtrl->show(); m_fileinfo = QString::fromUtf8(product->GetName().c_str()); m_fileinfo += GetFileSize(product->GetRelativePath().c_str()); - - AZ::Data::Asset imageAsset = Utils::LoadImageAsset(product->GetAssetId()); - IImageObjectPtr image = Utils::LoadImageFromImageAsset(imageAsset); - if (image) + CreateAndDisplayTextureItemAsync( + [assetId = product->GetAssetId()] + () -> CreateDisplayTextureResult { - // Add product image info - AZStd::string productInfo; - GetImageInfoString(imageAsset, productInfo); + AZ::Data::Asset imageAsset = Utils::LoadImageAsset(assetId); + IImageObjectPtr image = Utils::LoadImageFromImageAsset(imageAsset); - m_fileinfo += QStringLiteral("\r\n"); - m_fileinfo += productInfo.c_str(); + if (image) + { + // Add product image info + AZStd::string productInfo; + GetImageInfoString(imageAsset, productInfo); - m_previewImageObject = ConvertImageForPreview(image); - } + QString fileInfo = QStringLiteral("\r\n"); + fileInfo += productInfo.c_str(); + + return { ConvertImageForPreview(image), fileInfo }; + } + else + { + return { nullptr, "" }; + } + }); DisplayTextureItem(); } @@ -234,19 +249,28 @@ namespace ImageProcessingAtom m_fileinfo = QString::fromUtf8(source->GetName().c_str()); m_fileinfo += GetFileSize(source->GetFullPath().c_str()); - IImageObjectPtr image = IImageObjectPtr(LoadImageFromFile(source->GetFullPath())); - - if (image) + CreateAndDisplayTextureItemAsync( + [fullPath = source->GetFullPath()] + () -> CreateDisplayTextureResult { - // Add source image info - AZStd::string sourceInfo; - GetImageInfoString(image, sourceInfo); + IImageObjectPtr image = IImageObjectPtr(LoadImageFromFile(fullPath)); - m_fileinfo += QStringLiteral("\r\n"); - m_fileinfo += sourceInfo.c_str(); + if (image) + { + // Add source image info + AZStd::string sourceInfo; + GetImageInfoString(image, sourceInfo); - m_previewImageObject = ConvertImageForPreview(image); - } + QString fileInfo = QStringLiteral("\r\n"); + fileInfo += sourceInfo.c_str(); + + return { ConvertImageForPreview(image), fileInfo }; + } + else + { + return { nullptr, "" }; + } + }); DisplayTextureItem(); } @@ -284,6 +308,27 @@ namespace ImageProcessingAtom updateGeometry(); } + template + void ImagePreviewer::CreateAndDisplayTextureItemAsync(CreateFn create) + { + AZ::SystemTickBus::Handler::BusConnect(); + m_createDisplayTextureResult = QtConcurrent::run(AZStd::move(create)); + } + + void ImagePreviewer::OnSystemTick() + { + if (m_createDisplayTextureResult.isFinished()) + { + CreateDisplayTextureResult result = m_createDisplayTextureResult.result(); + m_previewImageObject = AZStd::move(result.first); + m_fileinfo += result.second; + + AZ::SystemTickBus::Handler::BusDisconnect(); + + DisplayTextureItem(); + } + } + void ImagePreviewer::PreviewSubImage(uint32_t mip) { QImage previewImage = GetSubImagePreview(m_previewImageObject, mip); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Previewer/ImagePreviewer.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Previewer/ImagePreviewer.h index f82a1cba5a..6888801d01 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Previewer/ImagePreviewer.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Previewer/ImagePreviewer.h @@ -8,12 +8,15 @@ #if !defined(Q_MOC_RUN) #include +#include #include #include #include #include +#include +#include #endif namespace Ui @@ -37,6 +40,7 @@ namespace ImageProcessingAtom { class ImagePreviewer : public AzToolsFramework::AssetBrowser::Previewer + , private AZ::SystemTickBus::Handler { Q_OBJECT public: @@ -60,16 +64,27 @@ namespace ImageProcessingAtom QString GetFileSize(const char* path); void DisplayTextureItem(); + template + void CreateAndDisplayTextureItemAsync(CreateFn create); + void PreviewSubImage(uint32_t mip); // QLabel word wrap does not break long words such as filenames, so manual word wrap needed static QString WordWrap(const QString& string, int maxLength); + // SystemTickBus + void OnSystemTick() override; + QScopedPointer m_ui; QString m_fileinfo; QString m_name = "ImagePreviewer"; // Decompressed image in preview. Cache it so we can preview its sub images IImageObjectPtr m_previewImageObject; + + // Properties for tracking the status of an asynchronous request to display an asset browser entry + using CreateDisplayTextureResult = AZStd::pair; + + QFuture m_createDisplayTextureResult; }; }//namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.cpp index 56ffc186e0..cc64eaeed9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -110,8 +111,7 @@ namespace ImageProcessingAtom void ImageThumbnailSystemComponent::RenderThumbnail( AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize) { - auto sourceKey = azrtti_cast(thumbnailKey.data()); - if (sourceKey) + if (auto sourceKey = azrtti_cast(thumbnailKey.data())) { bool foundIt = false; AZ::Data::AssetInfo assetInfo; @@ -124,52 +124,72 @@ namespace ImageProcessingAtom { AZStd::string fullPath; AZ::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), fullPath); - if (RenderThumbnailFromImage(thumbnailKey, thumbnailSize, IImageObjectPtr(LoadImageFromFile(fullPath)))) - { - return; - } + RenderThumbnailFromImage(thumbnailKey, thumbnailSize, + [fullPath]() { return IImageObjectPtr(LoadImageFromFile(fullPath)); } + ); } } - - auto productKey = azrtti_cast(thumbnailKey.data()); - if (productKey) + else if (auto productKey = azrtti_cast(thumbnailKey.data())) { - if (RenderThumbnailFromImage(thumbnailKey, thumbnailSize, Utils::LoadImageFromImageAsset(productKey->GetAssetId()))) - { - return; - } + RenderThumbnailFromImage(thumbnailKey, thumbnailSize, + [assetId = productKey->GetAssetId()]() { return Utils::LoadImageFromImageAsset(assetId); } + ); + } + else + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( + thumbnailKey, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender); } - - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( - thumbnailKey, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender); } - bool ImageThumbnailSystemComponent::RenderThumbnailFromImage( - AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize, IImageObjectPtr previewImage) const + template + void ImageThumbnailSystemComponent::RenderThumbnailFromImage( + AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize, MkImageFn mkPreviewImage) const { - if (!previewImage) + const auto JobRunner = [mkPreviewImage, thumbnailKey, thumbnailSize]() mutable { - return false; - } - - ImageToProcess imageToProcess(previewImage); - imageToProcess.ConvertFormat(ePixelFormat_R8G8B8A8); - previewImage = imageToProcess.Get(); - - AZ::u8* imageBuf = nullptr; - AZ::u32 mip = 0; - AZ::u32 pitch = 0; - previewImage->GetImagePointer(mip, imageBuf, pitch); - const AZ::u32 width = previewImage->GetWidth(mip); - const AZ::u32 height = previewImage->GetHeight(mip); - - QImage image(imageBuf, width, height, pitch, QImage::Format_RGBA8888); + IImageObjectPtr previewImage = mkPreviewImage(); + if (!previewImage) + { + AZ::SystemTickBus::QueueFunction( + [ + thumbnailKey + ]() + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( + thumbnailKey, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender); + }); - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( - thumbnailKey, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailRendered, - QPixmap::fromImage(image.scaled(QSize(thumbnailSize, thumbnailSize), Qt::KeepAspectRatio, Qt::SmoothTransformation))); + return; + } - return true; + ImageToProcess imageToProcess(previewImage); + imageToProcess.ConvertFormat(ePixelFormat_R8G8B8A8); + previewImage = imageToProcess.Get(); + + AZ::u8* imageBuf = nullptr; + AZ::u32 mip = 0; + AZ::u32 pitch = 0; + previewImage->GetImagePointer(mip, imageBuf, pitch); + const AZ::u32 width = previewImage->GetWidth(mip); + const AZ::u32 height = previewImage->GetHeight(mip); + + // Note that this image holds a non-owning pointer to the `previewImage' raw data buffer + const QImage image(imageBuf, width, height, pitch, QImage::Format_RGBA8888); + + // Dispatch event on main thread + AZ::SystemTickBus::QueueFunction( + [ + thumbnailKey, thumbnailSize, + pixmap = QPixmap::fromImage(image.scaled(QSize(thumbnailSize, thumbnailSize), Qt::KeepAspectRatio, Qt::SmoothTransformation)) + ]() mutable + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( + thumbnailKey, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailRendered, + pixmap); + }); + }; + AZ::CreateJobFunction(JobRunner, true)->Start(); } } // namespace Thumbnails } // namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.h index 2b5c722bd3..8830f282ca 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.h @@ -47,8 +47,9 @@ namespace ImageProcessingAtom bool Installed() const override; void RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize) override; - bool RenderThumbnailFromImage( - AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize, IImageObjectPtr previewImage) const; + template + void RenderThumbnailFromImage( + AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize, MkImageFn mkPreviewImage) const; }; } // namespace Thumbnails } // namespace ImageProcessingAtom