From: seungho Date: Wed, 24 Nov 2021 11:06:06 +0000 (+0900) Subject: Cache for the synchronous loading. X-Git-Tag: dali_2.1.3~9^2 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=60995c8e173400f1d080ca5f0e9385785206be29 Cache for the synchronous loading. Change-Id: Id793b3bcb72639d3c7a163196e29bdcf7d94718e Signed-off-by: seungho --- diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp index 04c51af..1553f97 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp @@ -71,7 +71,8 @@ public: TestObserver() : mCompleteType( CompleteType::NOT_COMPLETED ), mLoaded(false), - mObserverCalled(false) + mObserverCalled(false), + mTextureSet() { } @@ -81,6 +82,7 @@ public: mCompleteType = CompleteType::UPLOAD_COMPLETE; mLoaded = loadSuccess; mObserverCalled = true; + mTextureSet = textureSet; } virtual void LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied ) override @@ -93,6 +95,7 @@ public: CompleteType mCompleteType; bool mLoaded; bool mObserverCalled; + TextureSet mTextureSet; }; @@ -590,3 +593,305 @@ int UtcTextureManagerUseInvalidMask(void) END_TEST; } + +int UtcTextureManagerSynchronousLoadingFail(void) +{ + ToolkitTestApplication application; + tet_infoline( "UtcTextureManagerSynchronousLoadingFail" ); + + TextureManager textureManager; // Create new texture manager + + std::string maskname(""); + TextureManager::MaskingDataPointer maskInfo = nullptr; + maskInfo.reset(new TextureManager::MaskingData()); + maskInfo->mAlphaMaskUrl = maskname; + maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID; + maskInfo->mCropToMask = true; + maskInfo->mContentScaleFactor = 1.0f; + + std::string filename("dummy"); + auto textureId( TextureManager::INVALID_TEXTURE_ID ); + Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f ); + Dali::ImageDimensions atlasRectSize( 0,0 ); + bool atlasingStatus(false); + bool loadingStatus(false); + auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + ImageAtlasManagerPtr atlasManager = nullptr; + Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr; + + // load image synchronously. + TestObserver observer; + TextureSet textureSet = textureManager.LoadTexture( + filename, + ImageDimensions(), + FittingMode::SCALE_TO_FILL, + SamplingMode::BOX_THEN_LINEAR, + maskInfo, + true, // synchronous loading. + textureId, + atlasRect, + atlasRectSize, + atlasingStatus, + loadingStatus, + WrapMode::DEFAULT, + WrapMode::DEFAULT, + &observer, + atlasUploadObserver, + atlasManager, + true, + TextureManager::ReloadPolicy::CACHED, + preMultiply + ); + + DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION); + DALI_TEST_CHECK(!textureSet); // texture loading fail. + DALI_TEST_CHECK(textureId == TextureManager::INVALID_TEXTURE_ID); // invalid texture id is returned. + + END_TEST; +} + +int UtcTextureManagerCachingSynchronousLoading(void) +{ + ToolkitTestApplication application; + tet_infoline( "UtcTextureManagerCachingSynchronousLoading" ); + + TextureManager textureManager; // Create new texture manager + + std::string filename( TEST_IMAGE_FILE_NAME ); + + std::string maskname(""); + TextureManager::MaskingDataPointer maskInfo = nullptr; + maskInfo.reset(new TextureManager::MaskingData()); + maskInfo->mAlphaMaskUrl = maskname; + maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID; + maskInfo->mCropToMask = true; + maskInfo->mContentScaleFactor = 1.0f; + + Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f ); + Dali::ImageDimensions atlasRectSize( 0,0 ); + bool atlasingStatus(false); + bool loadingStatus(false); + auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + ImageAtlasManagerPtr atlasManager = nullptr; + Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr; + + // load image synchronously. + TestObserver observer; + auto textureId( TextureManager::INVALID_TEXTURE_ID ); + TextureSet textureSet = textureManager.LoadTexture( + filename, + ImageDimensions(), + FittingMode::SCALE_TO_FILL, + SamplingMode::BOX_THEN_LINEAR, + maskInfo, + true, // synchronous loading. + textureId, + atlasRect, + atlasRectSize, + atlasingStatus, + loadingStatus, + WrapMode::DEFAULT, + WrapMode::DEFAULT, + &observer, + atlasUploadObserver, + atlasManager, + true, + TextureManager::ReloadPolicy::CACHED, + preMultiply + ); + + DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION); + DALI_TEST_CHECK(textureSet); // texture is loaded. + + // observer isn't called in synchronous loading. + DALI_TEST_EQUALS(observer.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer.mObserverCalled, false, TEST_LOCATION); + + + // load same image asynchronously. + TestObserver asyncObserver; + auto asyncTextureId( TextureManager::INVALID_TEXTURE_ID ); + loadingStatus = false; + TextureSet asyncTextureSet = textureManager.LoadTexture( + filename, + ImageDimensions(), + FittingMode::SCALE_TO_FILL, + SamplingMode::BOX_THEN_LINEAR, + maskInfo, + false, // asynchronous loading. + asyncTextureId, + atlasRect, + atlasRectSize, + atlasingStatus, + loadingStatus, + WrapMode::DEFAULT, + WrapMode::DEFAULT, + &asyncObserver, + atlasUploadObserver, + atlasManager, + true, + TextureManager::ReloadPolicy::CACHED, + preMultiply + ); + + DALI_TEST_EQUALS(asyncTextureId, textureId, TEST_LOCATION); // texture is loaded. + DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION); + DALI_TEST_CHECK(asyncTextureSet); // Cached texture. + + // observer is directly called because textureSet is retrieved by cache. + DALI_TEST_EQUALS(asyncObserver.mLoaded, true, TEST_LOCATION); + DALI_TEST_EQUALS(asyncObserver.mObserverCalled, true, TEST_LOCATION); + + END_TEST; +} + +int UtcTextureManagerAsyncSyncAsync(void) +{ + ToolkitTestApplication application; + tet_infoline( "UtcTextureManagerAsyncSyncAsync" ); + + TextureManager textureManager; // Create new texture manager + + std::string filename( TEST_IMAGE_FILE_NAME ); + + std::string maskname(""); + TextureManager::MaskingDataPointer maskInfo = nullptr; + maskInfo.reset(new TextureManager::MaskingData()); + maskInfo->mAlphaMaskUrl = maskname; + maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID; + maskInfo->mCropToMask = true; + maskInfo->mContentScaleFactor = 1.0f; + + Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f ); + Dali::ImageDimensions atlasRectSize( 0,0 ); + bool atlasingStatus(false); + auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + ImageAtlasManagerPtr atlasManager = nullptr; + Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr; + + // load image asynchronously. + TestObserver asyncObserver1; + auto asyncTextureId1( TextureManager::INVALID_TEXTURE_ID ); + bool asyncLoadingStatus1 = false; + TextureSet asyncTextureSet1 = textureManager.LoadTexture( + filename, + ImageDimensions(), + FittingMode::SCALE_TO_FILL, + SamplingMode::BOX_THEN_LINEAR, + maskInfo, + false, // asynchronous loading. + asyncTextureId1, + atlasRect, + atlasRectSize, + atlasingStatus, + asyncLoadingStatus1, + WrapMode::DEFAULT, + WrapMode::DEFAULT, + &asyncObserver1, + atlasUploadObserver, + atlasManager, + true, + TextureManager::ReloadPolicy::CACHED, + preMultiply + ); + + DALI_TEST_EQUALS(asyncLoadingStatus1, true, TEST_LOCATION); // texture is loading now. + DALI_TEST_CHECK(!asyncTextureSet1); // texture is not loaded yet. + + // observer is still not called. + DALI_TEST_EQUALS(asyncObserver1.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, false, TEST_LOCATION); + + + // load same image synchronously just after asynchronous loading. + TestObserver syncObserver; + auto textureId( TextureManager::INVALID_TEXTURE_ID ); + bool syncLoadingStatus = false; + TextureSet syncTextureSet = textureManager.LoadTexture( + filename, + ImageDimensions(), + FittingMode::SCALE_TO_FILL, + SamplingMode::BOX_THEN_LINEAR, + maskInfo, + true, // synchronous loading. + textureId, + atlasRect, + atlasRectSize, + atlasingStatus, + syncLoadingStatus, + WrapMode::DEFAULT, + WrapMode::DEFAULT, + &syncObserver, + atlasUploadObserver, + atlasManager, + true, + TextureManager::ReloadPolicy::CACHED, + preMultiply + ); + + DALI_TEST_EQUALS(asyncTextureId1, textureId, TEST_LOCATION); // texture is loaded. + DALI_TEST_EQUALS(syncLoadingStatus, false, TEST_LOCATION); // texture is loaded. + DALI_TEST_CHECK(syncTextureSet); // texture is loaded. + + // syncObserver isn't called in synchronous loading. + DALI_TEST_EQUALS(syncObserver.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(syncObserver.mObserverCalled, false, TEST_LOCATION); + + // asyncObserver1 is still not called too. + DALI_TEST_EQUALS(asyncObserver1.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, false, TEST_LOCATION); + + + + // load image asynchronously. + TestObserver asyncObserver2; + auto asyncTextureId2( TextureManager::INVALID_TEXTURE_ID ); + bool asyncLoadingStatus2 = false; + TextureSet asyncTextureSet2 = textureManager.LoadTexture( + filename, + ImageDimensions(), + FittingMode::SCALE_TO_FILL, + SamplingMode::BOX_THEN_LINEAR, + maskInfo, + false, // asynchronous loading. + asyncTextureId2, + atlasRect, + atlasRectSize, + atlasingStatus, + asyncLoadingStatus2, + WrapMode::DEFAULT, + WrapMode::DEFAULT, + &asyncObserver2, + atlasUploadObserver, + atlasManager, + true, + TextureManager::ReloadPolicy::CACHED, + preMultiply + ); + + DALI_TEST_EQUALS(asyncLoadingStatus2, false, TEST_LOCATION); // texture is loaded by previous sync request + DALI_TEST_CHECK(asyncTextureSet2); // texture is loaded + DALI_TEST_CHECK(asyncTextureSet2 == syncTextureSet); // check loaded two texture is same. + + // observer is called synchronously because the texture is cached. + DALI_TEST_EQUALS(asyncObserver2.mLoaded, true, TEST_LOCATION); + DALI_TEST_EQUALS(asyncObserver2.mObserverCalled, true, TEST_LOCATION); + + asyncObserver2.mLoaded = false; + asyncObserver2.mObserverCalled = false; + + application.SendNotification(); + application.Render(); + + // Requested asynchronous loading at first is finished now and async observer is called now. + DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION ); + DALI_TEST_EQUALS(asyncObserver1.mLoaded, true, TEST_LOCATION); + DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, true, TEST_LOCATION); + DALI_TEST_CHECK(asyncObserver1.mTextureSet == asyncTextureSet2); // check loaded two texture is same. + + // asyncObserver2 was already called so it isn't called here. + DALI_TEST_EQUALS(asyncObserver2.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(asyncObserver2.mObserverCalled, false, TEST_LOCATION); + + END_TEST; +} diff --git a/dali-toolkit/internal/visuals/image-atlas-manager.cpp b/dali-toolkit/internal/visuals/image-atlas-manager.cpp index a8de5a8..577a433 100644 --- a/dali-toolkit/internal/visuals/image-atlas-manager.cpp +++ b/dali-toolkit/internal/visuals/image-atlas-manager.cpp @@ -46,6 +46,23 @@ ImageAtlasManager::~ImageAtlasManager() { } +bool ImageAtlasManager::CheckAtlasAvailable(const VisualUrl& url, const ImageDimensions& size) const +{ + ImageDimensions dimensions = size; + ImageDimensions zero; + if(size == zero) + { + dimensions = Dali::GetClosestImageSize(url.GetUrl()); + } + + // big image, atlasing is not applied + if(static_cast(dimensions.GetWidth()) * static_cast(dimensions.GetHeight()) > MAX_ITEM_AREA || dimensions.GetWidth() > DEFAULT_ATLAS_SIZE || dimensions.GetHeight() > DEFAULT_ATLAS_SIZE) + { + return false; + } + return true; +} + TextureSet ImageAtlasManager::Add(Vector4& textureRect, const VisualUrl& url, ImageDimensions& size, diff --git a/dali-toolkit/internal/visuals/image-atlas-manager.h b/dali-toolkit/internal/visuals/image-atlas-manager.h index b8f5a11..d2630c1 100644 --- a/dali-toolkit/internal/visuals/image-atlas-manager.h +++ b/dali-toolkit/internal/visuals/image-atlas-manager.h @@ -52,6 +52,15 @@ public: ImageAtlasManager(); /** + * @brief Check whether the image of url could be Atlas or not. + * + * @param [in] url The URL of the resource image file to use. + * @param [in] size The width and height to fit the loaded image to. + * @return True if the image could be Atlas. + */ + bool CheckAtlasAvailable(const VisualUrl& url, const ImageDimensions& size) const; + + /** * @brief Add an image to the atlas. * * @note To make the atlasing efficient, an valid size should be provided. diff --git a/dali-toolkit/internal/visuals/texture-manager-impl.cpp b/dali-toolkit/internal/visuals/texture-manager-impl.cpp index 093954e..49867c4 100644 --- a/dali-toolkit/internal/visuals/texture-manager-impl.cpp +++ b/dali-toolkit/internal/visuals/texture-manager-impl.cpp @@ -122,8 +122,10 @@ TextureManager::MaskingData::MaskingData() } TextureManager::TextureManager() -: mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); }), - mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); }), +: mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]() + { return AsyncLoadingHelper(*this); }), + mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]() + { return AsyncLoadingHelper(*this); }), mExternalTextures(), mLifecycleObservers(), mLoadQueue(), @@ -173,7 +175,7 @@ TextureSet TextureManager::LoadAnimatedImageTexture( else { auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - textureId = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex); + textureId = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex, false); TextureManager::LoadState loadState = GetTextureStateInternal(textureId); if(loadState == TextureManager::LoadState::UPLOADED) { @@ -220,7 +222,7 @@ Devel::PixelBuffer TextureManager::LoadPixelBuffer( } else { - RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u); + RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false); } return pixelBuffer; @@ -251,113 +253,100 @@ TextureSet TextureManager::LoadTexture( } } } - else if(synchronousLoading) + else { - PixelData data; - if(url.IsValid()) + // For Atlas + if(synchronousLoading && atlasingStatus && imageAtlasManager->CheckAtlasAvailable(url, desiredSize)) { - Devel::PixelBuffer pixelBuffer; - if(url.IsBufferResource()) - { - const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(url.GetUrl()); - if(encodedImageBuffer) - { - pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection); - } - } - else - { - pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection); - } + Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection); + if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid()) { - Devel::PixelBuffer maskPixelBuffer = LoadImageFromFile(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true); + Devel::PixelBuffer maskPixelBuffer = LoadImageSynchronously(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true); if(maskPixelBuffer) { pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask); } } + + PixelData data; if(pixelBuffer) { PreMultiply(pixelBuffer, preMultiplyOnLoad); data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer + + if(data) + { + textureSet = imageAtlasManager->Add(textureRect, data); + if(textureSet) + { + textureRectSize.SetWidth(data.GetWidth()); + textureRectSize.SetHeight(data.GetHeight()); + } + } + else + { + DALI_LOG_ERROR("TextureManager::LoadTexture: Synchronous Texture loading with atlasing is failed.\n"); + } } - } - if(!data) - { - DALI_LOG_ERROR("TextureManager::LoadTexture: Synchronous loading is failed\n"); - } - else - { - if(atlasingStatus) // attempt atlasing - { - textureSet = imageAtlasManager->Add(textureRect, data); - } - if(!textureSet) // big image, no atlasing or atlasing failed - { - atlasingStatus = false; - Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(), data.GetWidth(), data.GetHeight()); - texture.Upload(data); - textureSet = TextureSet::New(); - textureSet.SetTexture(0u, texture); - } - else + if(!textureSet) { - textureRectSize.SetWidth(data.GetWidth()); - textureRectSize.SetHeight(data.GetHeight()); + atlasingStatus = false; } } - } - else - { - loadingStatus = true; - if(atlasingStatus) - { - textureSet = imageAtlasManager->Add(textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver); - } - if(!textureSet) // big image, no atlasing or atlasing failed + + if(!textureSet) { - atlasingStatus = false; - if(!maskInfo || !maskInfo->mAlphaMaskUrl.IsValid()) + loadingStatus = true; + if(atlasingStatus) { - textureId = RequestLoad(url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad); + textureSet = imageAtlasManager->Add(textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver); } - else + if(!textureSet) // big image, no atlasing or atlasing failed { - maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl); - textureId = RequestLoad(url, - maskInfo->mAlphaMaskId, - maskInfo->mContentScaleFactor, - desiredSize, - fittingMode, - samplingMode, - TextureManager::NO_ATLAS, - maskInfo->mCropToMask, - textureObserver, - orientationCorrection, - reloadPolicy, - preMultiplyOnLoad); - } + atlasingStatus = false; + if(!maskInfo || !maskInfo->mAlphaMaskUrl.IsValid()) + { + textureId = RequestLoad(url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad, synchronousLoading); + } + else + { + maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, synchronousLoading); + textureId = RequestLoad(url, + maskInfo->mAlphaMaskId, + maskInfo->mContentScaleFactor, + desiredSize, + fittingMode, + samplingMode, + TextureManager::NO_ATLAS, + maskInfo->mCropToMask, + textureObserver, + orientationCorrection, + reloadPolicy, + preMultiplyOnLoad, + synchronousLoading); + } + + TextureManager::LoadState loadState = GetTextureStateInternal(textureId); + if(loadState == TextureManager::LoadState::UPLOADED) + { + // UploadComplete has already been called - keep the same texture set + textureSet = GetTextureSet(textureId); + } - TextureManager::LoadState loadState = GetTextureStateInternal(textureId); - if(loadState == TextureManager::LoadState::UPLOADED) + // If we are loading the texture, or waiting for the ready signal handler to complete, inform + // caller that they need to wait. + loadingStatus = (loadState == TextureManager::LoadState::LOADING || + loadState == TextureManager::LoadState::WAITING_FOR_MASK || + loadState == TextureManager::LoadState::MASK_APPLYING || + loadState == TextureManager::LoadState::MASK_APPLIED || + loadState == TextureManager::LoadState::NOT_STARTED || + mQueueLoadFlag); + } + else { - // UploadComplete has already been called - keep the same texture set - textureSet = GetTextureSet(textureId); + textureRectSize = desiredSize; } - - // If we are loading the texture, or waiting for the ready signal handler to complete, inform - // caller that they need to wait. - loadingStatus = (loadState == TextureManager::LoadState::LOADING || - loadState == TextureManager::LoadState::WAITING_FOR_MASK || - loadState == TextureManager::LoadState::MASK_APPLYING || - loadState == TextureManager::LoadState::MASK_APPLIED || - loadState == TextureManager::LoadState::NOT_STARTED || - mQueueLoadFlag); - } - else - { - textureRectSize = desiredSize; } } @@ -368,6 +357,11 @@ TextureSet TextureManager::LoadTexture( textureSet.SetSampler(0u, sampler); } + if(synchronousLoading) + { + loadingStatus = false; + } + return textureSet; } @@ -380,9 +374,10 @@ TextureManager::TextureId TextureManager::RequestLoad( TextureUploadObserver* observer, bool orientationCorrection, TextureManager::ReloadPolicy reloadPolicy, - TextureManager::MultiplyOnLoad& preMultiplyOnLoad) + TextureManager::MultiplyOnLoad& preMultiplyOnLoad, + bool synchronousLoading) { - return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u); + return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading); } TextureManager::TextureId TextureManager::RequestLoad( @@ -397,16 +392,17 @@ TextureManager::TextureId TextureManager::RequestLoad( TextureUploadObserver* observer, bool orientationCorrection, TextureManager::ReloadPolicy reloadPolicy, - TextureManager::MultiplyOnLoad& preMultiplyOnLoad) + TextureManager::MultiplyOnLoad& preMultiplyOnLoad, + bool synchronousLoading) { - return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u); + return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading); } -TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl) +TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl, bool synchronousLoading) { // Use the normal load procedure to get the alpha mask. auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u); + return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading); } TextureManager::TextureId TextureManager::RequestLoadInternal( @@ -424,7 +420,8 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( TextureManager::ReloadPolicy reloadPolicy, TextureManager::MultiplyOnLoad& preMultiplyOnLoad, Dali::AnimatedImageLoading animatedImageLoading, - uint32_t frameIndex) + uint32_t frameIndex, + bool synchronousLoading) { // First check if the requested Texture is cached. bool isAnimatedImage = (animatedImageLoading) ? true : false; @@ -457,37 +454,30 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId); } - // Check if the requested Texture exist in Encoded Buffer - // This mean, that buffer is not cached, and need to be decoded. - if(textureId == INVALID_TEXTURE_ID && VisualUrl::BUFFER == url.GetProtocolType()) + if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required { - std::string location = url.GetLocation(); - if(location.size() > 0u) + if(VisualUrl::BUFFER == url.GetProtocolType()) { - TextureId targetId = std::stoi(location); - const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(targetId); - if(encodedImageBuffer) + std::string location = url.GetLocation(); + if(location.size() > 0u) { - textureId = targetId; - - // Increase EncodedImageBuffer reference during it contains mTextureInfoContainer. - UseExternalResource(url.GetUrl()); - - // Insert this buffer at mTextureInfoContainer. - // This buffer will decode at ImageLoaderThread. - bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD); - mTextureInfoContainer.push_back(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex)); - cacheIndex = mTextureInfoContainer.size() - 1u; + TextureId targetId = std::stoi(location); + const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(targetId); + if(encodedImageBuffer) + { + textureId = targetId; - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New buffered texture, cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId); + // Increase EncodedImageBuffer reference during it contains mTextureInfoContainer. + UseExternalResource(url.GetUrl()); + } } } - } - if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required - { - // We need a new Texture. - textureId = GenerateUniqueTextureId(); + if(textureId == INVALID_TEXTURE_ID) + { + textureId = GenerateUniqueTextureId(); + } + bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD); mTextureInfoContainer.push_back(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex)); cacheIndex = mTextureInfoContainer.size() - 1u; @@ -518,48 +508,101 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( textureInfo.loadState = TextureManager::LoadState::NOT_STARTED; } - // Check if we should add the observer. - // Only do this if we have not loaded yet and it will not have loaded by the end of this method. - switch(textureInfo.loadState) + if(!synchronousLoading) { - case TextureManager::LoadState::LOAD_FAILED: // Failed notifies observer which then stops observing. - case TextureManager::LoadState::NOT_STARTED: - { - LoadOrQueueTexture(textureInfo, observer); // If called inside NotifyObservers, queues until afterwards - break; - } - case TextureManager::LoadState::LOADING: - case TextureManager::LoadState::WAITING_FOR_MASK: - case TextureManager::LoadState::MASK_APPLYING: - case TextureManager::LoadState::MASK_APPLIED: + // Check if we should add the observer. + // Only do this if we have not loaded yet and it will not have loaded by the end of this method. + switch(textureInfo.loadState) { - ObserveTexture(textureInfo, observer); - break; - } - case TextureManager::LoadState::UPLOADED: - { - if(observer) + case TextureManager::LoadState::LOAD_FAILED: // Failed notifies observer which then stops observing. + case TextureManager::LoadState::NOT_STARTED: + { + LoadOrQueueTexture(textureInfo, observer); // If called inside NotifyObservers, queues until afterwards + break; + } + case TextureManager::LoadState::LOADING: + case TextureManager::LoadState::WAITING_FOR_MASK: + case TextureManager::LoadState::MASK_APPLYING: + case TextureManager::LoadState::MASK_APPLIED: { - LoadOrQueueTexture(textureInfo, observer); + ObserveTexture(textureInfo, observer); + break; + } + case TextureManager::LoadState::UPLOADED: + { + if(observer) + { + LoadOrQueueTexture(textureInfo, observer); + } + break; + } + case TextureManager::LoadState::CANCELLED: + { + // A cancelled texture hasn't finished loading yet. Treat as a loading texture + // (it's ref count has already been incremented, above) + textureInfo.loadState = TextureManager::LoadState::LOADING; + ObserveTexture(textureInfo, observer); + break; + } + case TextureManager::LoadState::LOAD_FINISHED: + { + // Loading has already completed. + if(observer && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER) + { + LoadOrQueueTexture(textureInfo, observer); + } + break; } - break; } - case TextureManager::LoadState::CANCELLED: + } + else + { + // If the image is already finished to load, use cached texture. + // We don't need to consider Observer becaouse this is synchronous loading. + if(textureInfo.loadState == TextureManager::LoadState::UPLOADED || + textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED) { - // A cancelled texture hasn't finished loading yet. Treat as a loading texture - // (it's ref count has already been incremented, above) - textureInfo.loadState = TextureManager::LoadState::LOADING; - ObserveTexture(textureInfo, observer); - break; + return textureId; } - case TextureManager::LoadState::LOAD_FINISHED: + else { - // Loading has already completed. - if(observer && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER) + Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection);; + + if(!pixelBuffer) { - LoadOrQueueTexture(textureInfo, observer); + // If pixelBuffer loading is failed in synchronously, call Remove() method. + Remove(textureId, nullptr); + return INVALID_TEXTURE_ID; + } + + if(storageType == StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading. + { + textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data + textureInfo.loadState = LoadState::LOAD_FINISHED; + } + else // For the image loading. + { + if(maskTextureId != INVALID_TEXTURE_ID) + { + int maskCacheIndex = GetCacheIndexFromId(maskTextureId); + if(maskCacheIndex != INVALID_CACHE_INDEX) + { + Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer; + if(maskPixelBuffer) + { + pixelBuffer.ApplyMask(maskPixelBuffer, contentScale, cropToMask); + } + } + else + { + DALI_LOG_ERROR("Mask image is not stored in cache.\n"); + } + } + PreMultiply(pixelBuffer, preMultiplyOnLoad); + + // Upload texture + UploadTexture(pixelBuffer, textureInfo); } - break; } } @@ -687,6 +730,28 @@ TextureManager::LoadState TextureManager::GetTextureStateInternal(TextureId text return loadState; } +Devel::PixelBuffer TextureManager::LoadImageSynchronously(const VisualUrl& url, + const ImageDimensions desiredSize, + FittingMode::Type fittingMode, + Dali::SamplingMode::Type samplingMode, + bool orientationCorrection) +{ + Devel::PixelBuffer pixelBuffer; + if(url.IsBufferResource()) + { + const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(url.GetUrl()); + if(encodedImageBuffer) + { + pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection); + } + } + else + { + pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection); + } + return pixelBuffer; +} + TextureSet TextureManager::GetTextureSet(TextureId textureId) { TextureSet textureSet; // empty handle @@ -759,7 +824,7 @@ std::string TextureManager::AddExternalEncodedImageBuffer(const EncodedImageBuff { // If same buffer added, increase reference count and return. elem.referenceCount++; - return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId));; + return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId)); } } TextureManager::EncodedBufferTextureInfo info(GenerateUniqueTextureId(), encodedImageBuffer); @@ -807,7 +872,7 @@ EncodedImageBuffer TextureManager::RemoveExternalEncodedImageBuffer(const std::s std::string location = VisualUrl::GetLocation(url); if(location.size() > 0u) { - TextureId id = std::stoi(location); + TextureId id = std::stoi(location); const auto end = mEncodedBufferTextures.end(); for(auto iter = mEncodedBufferTextures.begin(); iter != end; ++iter) { @@ -1049,7 +1114,10 @@ void TextureManager::PostLoad(TextureInfo& textureInfo, Devel::PixelBuffer& pixe // If there is a mask texture ID associated with this texture, then apply the mask // if it's already loaded. If it hasn't, and the mask is still loading, // wait for the mask to finish loading. - if(textureInfo.maskTextureId != INVALID_TEXTURE_ID) + // note, If the texture is already uploaded synchronously during loading, + // we don't need to apply mask. + if(textureInfo.loadState != LoadState::UPLOADED && + textureInfo.maskTextureId != INVALID_TEXTURE_ID) { if(textureInfo.loadState == LoadState::MASK_APPLYING) { @@ -1153,7 +1221,7 @@ void TextureManager::ApplyMask(TextureInfo& textureInfo, TextureId maskTextureId void TextureManager::UploadTexture(Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo) { - if(textureInfo.useAtlas != USE_ATLAS) + if(textureInfo.loadState != LoadState::UPLOADED && textureInfo.useAtlas != USE_ATLAS) { DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, " TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId); diff --git a/dali-toolkit/internal/visuals/texture-manager-impl.h b/dali-toolkit/internal/visuals/texture-manager-impl.h index 48eea78..7730d28 100644 --- a/dali-toolkit/internal/visuals/texture-manager-impl.h +++ b/dali-toolkit/internal/visuals/texture-manager-impl.h @@ -75,9 +75,10 @@ public: */ enum class StorageType : uint8_t { - KEEP_PIXEL_BUFFER, - RETURN_PIXEL_BUFFER, - UPLOAD_TO_TEXTURE + KEEP_PIXEL_BUFFER, ///< Keep loaded pixel buffer inside of texture manager without making texture. This could be used for inside pixel process like mask image. + RETURN_PIXEL_BUFFER, ///< Return loaded pixel buffer without making texture. + /// Because a pixel buffer cannot be used multiple texture, this pixel buffer only cached during loading, and is removed after loading is finished. + UPLOAD_TO_TEXTURE ///< Loaded image will be uploaded to texture and the texture will be returned. }; /** @@ -177,7 +178,6 @@ public: * * @return The texture set containing the frame of animated image, or empty if still loading. */ - TextureSet LoadAnimatedImageTexture(Dali::AnimatedImageLoading animatedImageLoading, uint32_t frameIndex, Dali::SamplingMode::Type samplingMode, @@ -206,7 +206,6 @@ public: * * @return The pixel buffer containing the image, or empty if still loading. */ - Devel::PixelBuffer LoadPixelBuffer(const VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode, @@ -253,7 +252,6 @@ public: * * @return The texture set containing the image, or empty if still loading. */ - TextureSet LoadTexture(const VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode, @@ -293,6 +291,7 @@ public: * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data * @param[in] reloadPolicy Forces a reload of the texture even if already cached * @param[in,out] preMultiplyOnLoad True if the image color should be multiplied by it's alpha. Set to false if the image has no alpha channel + * @param[in] synchronousLoading true if the frame should be loaded synchronously * @return A TextureId to use as a handle to reference this Texture */ TextureId RequestLoad(const VisualUrl& url, @@ -303,7 +302,8 @@ public: TextureUploadObserver* observer, bool orientationCorrection, TextureManager::ReloadPolicy reloadPolicy, - MultiplyOnLoad& preMultiplyOnLoad); + MultiplyOnLoad& preMultiplyOnLoad, + bool synchronousLoading = false); /** * @brief Requests an image load of the given URL, when the texture has @@ -335,6 +335,7 @@ public: * @param[in] reloadPolicy Forces a reload of the texture even if already cached * @param[in] preMultiplyOnLoad True if the image color should be multiplied by it's alpha. Set to false if the * image has no alpha channel + * @param[in] synchronousLoading true if the frame should be loaded synchronously * @return A TextureId to use as a handle to reference this Texture */ TextureId RequestLoad(const VisualUrl& url, @@ -348,13 +349,15 @@ public: TextureUploadObserver* observer, bool orientationCorrection, TextureManager::ReloadPolicy reloadPolicy, - MultiplyOnLoad& preMultiplyOnLoad); + MultiplyOnLoad& preMultiplyOnLoad, + bool synchronousLoading = false); /** * Requests a masking image to be loaded. This mask is not uploaded to GL, * instead, it is stored in CPU memory, and can be used for CPU blending. */ - TextureId RequestMaskLoad(const VisualUrl& maskUrl); + TextureId RequestMaskLoad(const VisualUrl& maskUrl, + bool synchronousLoading = false); /** * @brief Remove a Texture from the TextureManager. @@ -492,6 +495,7 @@ private: * there is no alpha * @param[in] animatedImageLoading The AnimatedImageLoading to load animated image * @param[in] frameIndex The frame index of a frame to be loaded frame + * @param[in] synchronousLoading true if the frame should be loaded synchronously * @return A TextureId to use as a handle to reference this Texture */ TextureId RequestLoadInternal( @@ -509,7 +513,8 @@ private: TextureManager::ReloadPolicy reloadPolicy, MultiplyOnLoad& preMultiplyOnLoad, Dali::AnimatedImageLoading animatedImageLoading, - uint32_t frameIndex); + uint32_t frameIndex, + bool synchronousLoading); /** * @brief Get the current state of a texture @@ -519,6 +524,23 @@ private: */ LoadState GetTextureStateInternal(TextureId textureId); + /** + * @brief Load a new image synchronously. + * @param[in] url The URL of the image to load + * @param[in] desiredSize The size the image is likely to appear at. + * This can be set to 0,0 for automatic + * @param[in] fittingMode The FittingMode to use + * @param[in] samplingMode The SamplingMode to use + * @param[in] orientationCorrection Whether to use image metadata to rotate or flip the image, + * e.g., from portrait to landscape + * @return PixelBuffer of loaded image. + */ + Devel::PixelBuffer LoadImageSynchronously(const VisualUrl& url, + const ImageDimensions desiredSize, + FittingMode::Type fittingMode, + Dali::SamplingMode::Type samplingMode, + bool orientationCorrection); + typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching. // Structs: