From 77cec61f0ccb6270ae149ee3e7f31418002e07f6 Mon Sep 17 00:00:00 2001 From: "Eunki, Hong" Date: Wed, 16 Feb 2022 20:27:33 +0900 Subject: [PATCH] Split texture-manager-impl files Collect all TextureManager relative files in one folder. And split TextureManager API's job as three files. - texture-manager-impl : Control texture loading it self. like alpha masking / synchronous / atlas / etc... - texture-cache-manager : Control internal texture caching container, external texture set, external encoded image buffers. Generate new TextureId, and we can access TextureInfo by TextureCacheIndex. - texture-async-loading-helper : Help async loader thread create, and callback load finished. And make most API's input parameters from 'T' to 'const T&' if we can. And also, make TextureManagerType namespace s.t. texture-manager-impl and textuer-cache-manager both class need to be use, and should be shared each other. Change-Id: Ia522a7b36da51d64282ca6b9ab4190a4e0476ad1 Signed-off-by: Eunki, Hong --- .../utc-Dali-TextureManager.cpp | 398 +++-- dali-toolkit/internal/file.list | 6 +- .../internal/image-loader/image-url-impl.cpp | 6 +- .../texture-async-loading-helper.cpp | 125 ++ .../texture-manager/texture-async-loading-helper.h | 145 ++ .../texture-manager/texture-cache-manager.cpp | 502 +++++++ .../texture-manager/texture-cache-manager.h | 320 ++++ .../texture-manager/texture-manager-impl.cpp | 1108 ++++++++++++++ .../texture-manager/texture-manager-impl.h | 627 ++++++++ .../texture-manager/texture-manager-type.h | 206 +++ .../texture-upload-observer.cpp | 6 +- .../texture-upload-observer.h | 21 +- .../visuals/animated-image/fixed-image-cache.h | 4 +- .../internal/visuals/animated-image/image-cache.h | 6 +- .../animated-image/rolling-animated-image-cache.h | 4 +- .../visuals/animated-image/rolling-image-cache.h | 5 +- .../internal/visuals/image/image-visual.cpp | 2 +- dali-toolkit/internal/visuals/image/image-visual.h | 4 +- dali-toolkit/internal/visuals/npatch-data.h | 4 +- dali-toolkit/internal/visuals/npatch-loader.h | 4 +- .../internal/visuals/npatch/npatch-visual.h | 4 +- .../internal/visuals/texture-manager-impl.cpp | 1555 -------------------- .../internal/visuals/texture-manager-impl.h | 957 ------------ .../internal/visuals/visual-factory-cache.h | 34 +- 24 files changed, 3281 insertions(+), 2772 deletions(-) create mode 100644 dali-toolkit/internal/texture-manager/texture-async-loading-helper.cpp create mode 100644 dali-toolkit/internal/texture-manager/texture-async-loading-helper.h create mode 100644 dali-toolkit/internal/texture-manager/texture-cache-manager.cpp create mode 100644 dali-toolkit/internal/texture-manager/texture-cache-manager.h create mode 100644 dali-toolkit/internal/texture-manager/texture-manager-impl.cpp create mode 100644 dali-toolkit/internal/texture-manager/texture-manager-impl.h create mode 100644 dali-toolkit/internal/texture-manager/texture-manager-type.h rename dali-toolkit/internal/{visuals => texture-manager}/texture-upload-observer.cpp (89%) rename dali-toolkit/internal/{visuals => texture-manager}/texture-upload-observer.h (78%) delete mode 100644 dali-toolkit/internal/visuals/texture-manager-impl.cpp delete mode 100644 dali-toolkit/internal/visuals/texture-manager-impl.h 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 d279482..6ec8617 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,13 +20,15 @@ #include #include -#include #include -#include -#include -#include +#include + +#include +#include +#include #include #include ///< For VisualFactory's member TextureManager. +#include #include @@ -38,7 +40,7 @@ using namespace Dali::Toolkit::Internal; void utc_dali_toolkit_texture_manager_startup(void) { - setenv( "LOG_TEXTURE_MANAGER", "3", 1 ); + setenv("LOG_TEXTURE_MANAGER", "3", 1); test_return_value = TET_UNDEF; #if defined(ELDBUS_ENABLED) DBusWrapper::Install(std::unique_ptr(new TestDBusWrapper)); @@ -52,8 +54,7 @@ void utc_dali_toolkit_texture_manager_cleanup(void) namespace { - -const char* TEST_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/gallery-small-1.jpg"; +const char* TEST_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/gallery-small-1.jpg"; } @@ -69,14 +70,14 @@ public: public: TestObserver() - : mCompleteType( CompleteType::NOT_COMPLETED ), + : mCompleteType(CompleteType::NOT_COMPLETED), mLoaded(false), mObserverCalled(false), mTextureSet() { } - virtual void LoadComplete( bool loadSuccess, TextureInformation textureInformation ) override + virtual void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override { if(textureInformation.returnType == TextureUploadObserver::ReturnType::TEXTURE) { @@ -86,41 +87,40 @@ public: { mCompleteType = CompleteType::LOAD_COMPLETE; } - mLoaded = loadSuccess; + mLoaded = loadSuccess; mObserverCalled = true; - mTextureSet = textureInformation.textureSet; + mTextureSet = textureInformation.textureSet; } CompleteType mCompleteType; - bool mLoaded; - bool mObserverCalled; - TextureSet mTextureSet; + bool mLoaded; + bool mObserverCalled; + TextureSet mTextureSet; }; - int UtcTextureManagerRequestLoad(void) { ToolkitTestApplication application; TextureManager textureManager; // Create new texture manager - TestObserver observer; - std::string filename("image.png"); - auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - TextureManager::TextureId textureId = textureManager.RequestLoad( + TestObserver observer; + std::string filename("image.png"); + auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + TextureManager::TextureId textureId = textureManager.RequestLoad( filename, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, - TextureManager::NO_ATLAS, + TextureManager::UseAtlas::NO_ATLAS, &observer, true, TextureManager::ReloadPolicy::CACHED, preMultiply); - VisualUrl url = textureManager.GetVisualUrl( textureId ); + VisualUrl url = textureManager.GetVisualUrl(textureId); - DALI_TEST_EQUALS( url.GetUrl().compare( filename ), 0, TEST_LOCATION ); + DALI_TEST_EQUALS(url.GetUrl().compare(filename), 0, TEST_LOCATION); END_TEST; } @@ -131,23 +131,23 @@ int UtcTextureManagerGenerateHash(void) TextureManager textureManager; // Create new texture manager - TestObserver observer; - std::string filename( "image.png" ); - auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - TextureManager::TextureId textureId = textureManager.RequestLoad( + TestObserver observer; + std::string filename("image.png"); + auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + TextureManager::TextureId textureId = textureManager.RequestLoad( filename, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, - TextureManager::USE_ATLAS, + TextureManager::UseAtlas::NO_ATLAS, &observer, true, TextureManager::ReloadPolicy::CACHED, preMultiply); - VisualUrl url = textureManager.GetVisualUrl( textureId ); + VisualUrl url = textureManager.GetVisualUrl(textureId); - DALI_TEST_EQUALS( url.GetUrl().compare( filename ), 0, TEST_LOCATION ); + DALI_TEST_EQUALS(url.GetUrl().compare(filename), 0, TEST_LOCATION); END_TEST; } @@ -155,7 +155,7 @@ int UtcTextureManagerGenerateHash(void) int UtcTextureManagerEncodedImageBuffer(void) { ToolkitTestApplication application; - tet_infoline( "UtcTextureManagerEncodedImageBuffer" ); + tet_infoline("UtcTextureManagerEncodedImageBuffer"); auto visualFactory = Toolkit::VisualFactory::Get(); auto& textureManager = GetImplementation(visualFactory).GetTextureManager(); // Use VisualFactory's texture manager @@ -164,9 +164,9 @@ int UtcTextureManagerEncodedImageBuffer(void) EncodedImageBuffer buffer1 = ConvertFileToEncodedImageBuffer(TEST_IMAGE_FILE_NAME); EncodedImageBuffer buffer2 = ConvertFileToEncodedImageBuffer(TEST_IMAGE_FILE_NAME); - std::string url1 = textureManager.AddExternalEncodedImageBuffer(buffer1); - std::string url2 = textureManager.AddExternalEncodedImageBuffer(buffer1); - std::string url3 = VisualUrl::CreateBufferUrl(""); ///< Impossible Buffer URL. for coverage + std::string url1 = textureManager.AddExternalEncodedImageBuffer(buffer1); + std::string url2 = textureManager.AddExternalEncodedImageBuffer(buffer1); + std::string url3 = VisualUrl::CreateBufferUrl(""); ///< Impossible Buffer URL. for coverage // Check if same EncodedImageBuffer get same url DALI_TEST_CHECK(url1 == url2); @@ -187,26 +187,26 @@ int UtcTextureManagerEncodedImageBuffer(void) ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, - TextureManager::NO_ATLAS, + TextureManager::UseAtlas::NO_ATLAS, &observer1, true, ///< orientationCorrection TextureManager::ReloadPolicy::CACHED, preMultiply); - DALI_TEST_EQUALS( observer1.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer1.mObserverCalled, false, TEST_LOCATION ); + DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION ); + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( observer1.mLoaded, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer1.mObserverCalled, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer1.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION ); + DALI_TEST_EQUALS(observer1.mLoaded, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer1.mObserverCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer1.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION); TestObserver observer2; // Syncload @@ -220,9 +220,9 @@ int UtcTextureManagerEncodedImageBuffer(void) true, ///< orientationCorrection preMultiply); - DALI_TEST_CHECK( pixelBuffer ); - DALI_TEST_EQUALS( observer2.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer2.mObserverCalled, false, TEST_LOCATION ); + DALI_TEST_CHECK(pixelBuffer); + DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer2.mObserverCalled, false, TEST_LOCATION); // Asyncload pixelBuffer = textureManager.LoadPixelBuffer( @@ -235,20 +235,20 @@ int UtcTextureManagerEncodedImageBuffer(void) true, ///< orientationCorrection preMultiply); - DALI_TEST_EQUALS( observer2.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer2.mObserverCalled, false, TEST_LOCATION ); + DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer2.mObserverCalled, false, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION ); + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( observer2.mLoaded, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer2.mObserverCalled, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer2.mCompleteType, TestObserver::CompleteType::LOAD_COMPLETE, TEST_LOCATION ); + DALI_TEST_EQUALS(observer2.mLoaded, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer2.mObserverCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer2.mCompleteType, TestObserver::CompleteType::LOAD_COMPLETE, TEST_LOCATION); textureManager.RemoveExternalEncodedImageBuffer(url1); textureManager.RemoveExternalEncodedImageBuffer(url2); @@ -261,16 +261,16 @@ int UtcTextureManagerEncodedImageBuffer(void) ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, - TextureManager::NO_ATLAS, + TextureManager::UseAtlas::NO_ATLAS, &observer3, true, ///< orientationCorrection TextureManager::ReloadPolicy::CACHED, preMultiply); // Load will be success because url1 is cached - DALI_TEST_EQUALS( observer3.mLoaded, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer3.mObserverCalled, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer3.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION ); + DALI_TEST_EQUALS(observer3.mLoaded, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer3.mObserverCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer3.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION); TestObserver observer4; textureManager.RequestLoad( @@ -278,26 +278,26 @@ int UtcTextureManagerEncodedImageBuffer(void) ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, - TextureManager::NO_ATLAS, + TextureManager::UseAtlas::NO_ATLAS, &observer4, true, ///< orientationCorrection TextureManager::ReloadPolicy::FORCED, preMultiply); - DALI_TEST_EQUALS( observer4.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer4.mObserverCalled, false, TEST_LOCATION ); + DALI_TEST_EQUALS(observer4.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer4.mObserverCalled, false, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION ); + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); application.SendNotification(); application.Render(); // Load will be failed becuase reloadpolicy is forced - DALI_TEST_EQUALS( observer4.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer4.mObserverCalled, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer4.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION ); + DALI_TEST_EQUALS(observer4.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer4.mObserverCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer4.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION); TestObserver observer5; pixelBuffer = textureManager.LoadPixelBuffer( @@ -311,9 +311,9 @@ int UtcTextureManagerEncodedImageBuffer(void) preMultiply); // Load will be faild because synchronousLoading doesn't use cached texture - DALI_TEST_CHECK( !pixelBuffer ); - DALI_TEST_EQUALS( observer5.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer5.mObserverCalled, false, TEST_LOCATION ); + DALI_TEST_CHECK(!pixelBuffer); + DALI_TEST_EQUALS(observer5.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer5.mObserverCalled, false, TEST_LOCATION); TestObserver observer6; pixelBuffer = textureManager.LoadPixelBuffer( @@ -326,21 +326,21 @@ int UtcTextureManagerEncodedImageBuffer(void) true, ///< orientationCorrection preMultiply); - DALI_TEST_EQUALS( observer6.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer6.mObserverCalled, false, TEST_LOCATION ); + DALI_TEST_EQUALS(observer6.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer6.mObserverCalled, false, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION ); + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); application.SendNotification(); application.Render(); // Load will be failed because url3 is invalid URL - DALI_TEST_EQUALS( observer6.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer6.mObserverCalled, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer6.mCompleteType, TestObserver::CompleteType::LOAD_COMPLETE, TEST_LOCATION ); + DALI_TEST_EQUALS(observer6.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer6.mObserverCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer6.mCompleteType, TestObserver::CompleteType::LOAD_COMPLETE, TEST_LOCATION); END_TEST; } @@ -348,7 +348,7 @@ int UtcTextureManagerEncodedImageBuffer(void) int UtcTextureManagerEncodedImageBufferReferenceCount(void) { ToolkitTestApplication application; - tet_infoline( "UtcTextureManagerEncodedImageBuffer check reference count works well" ); + tet_infoline("UtcTextureManagerEncodedImageBuffer check reference count works well"); auto visualFactory = Toolkit::VisualFactory::Get(); auto& textureManager = GetImplementation(visualFactory).GetTextureManager(); // Use VisualFactory's texture manager @@ -357,8 +357,8 @@ int UtcTextureManagerEncodedImageBufferReferenceCount(void) EncodedImageBuffer buffer1 = ConvertFileToEncodedImageBuffer(TEST_IMAGE_FILE_NAME); EncodedImageBuffer buffer2 = ConvertFileToEncodedImageBuffer(TEST_IMAGE_FILE_NAME); - std::string url1 = textureManager.AddExternalEncodedImageBuffer(buffer1); - std::string url2 = textureManager.AddExternalEncodedImageBuffer(buffer1); + std::string url1 = textureManager.AddExternalEncodedImageBuffer(buffer1); + std::string url2 = textureManager.AddExternalEncodedImageBuffer(buffer1); // Check if same EncodedImageBuffer get same url DALI_TEST_CHECK(url1 == url2); @@ -396,29 +396,29 @@ int UtcTextureManagerEncodedImageBufferReferenceCount(void) ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, - TextureManager::NO_ATLAS, + TextureManager::UseAtlas::NO_ATLAS, &observer1, true, ///< orientationCorrection TextureManager::ReloadPolicy::CACHED, preMultiply); - DALI_TEST_EQUALS( observer1.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer1.mObserverCalled, false, TEST_LOCATION ); + DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION ); + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( observer1.mLoaded, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer1.mObserverCalled, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer1.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION ); + DALI_TEST_EQUALS(observer1.mLoaded, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer1.mObserverCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer1.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION); // LoadPixelBuffer doen't use cache. url2 will not be cached - TestObserver observer2; + TestObserver observer2; Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer( url2, ImageDimensions(), @@ -429,20 +429,20 @@ int UtcTextureManagerEncodedImageBufferReferenceCount(void) true, ///< orientationCorrection preMultiply); - DALI_TEST_EQUALS( observer2.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer2.mObserverCalled, false, TEST_LOCATION ); + DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer2.mObserverCalled, false, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION ); + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( observer2.mLoaded, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer2.mObserverCalled, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer2.mCompleteType, TestObserver::CompleteType::LOAD_COMPLETE, TEST_LOCATION ); + DALI_TEST_EQUALS(observer2.mLoaded, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer2.mObserverCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer2.mCompleteType, TestObserver::CompleteType::LOAD_COMPLETE, TEST_LOCATION); // Decrease each url's reference count. textureManager.RemoveExternalEncodedImageBuffer(url1); @@ -460,44 +460,43 @@ int UtcTextureManagerEncodedImageBufferReferenceCount(void) END_TEST; } - int UtcTextureManagerCachingForDifferentLoadingType(void) { ToolkitTestApplication application; - tet_infoline( "UtcTextureManagerCachingForDifferentLoadingType" ); + tet_infoline("UtcTextureManagerCachingForDifferentLoadingType"); TextureManager textureManager; // Create new texture manager TestObserver observer1; - std::string filename( TEST_IMAGE_FILE_NAME ); - auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + std::string filename(TEST_IMAGE_FILE_NAME); + auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; textureManager.RequestLoad( filename, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, - TextureManager::NO_ATLAS, + TextureManager::UseAtlas::NO_ATLAS, &observer1, true, TextureManager::ReloadPolicy::CACHED, preMultiply); - DALI_TEST_EQUALS( observer1.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer1.mObserverCalled, false, TEST_LOCATION ); + DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION ); + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( observer1.mLoaded, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer1.mObserverCalled, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer1.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION ); + DALI_TEST_EQUALS(observer1.mLoaded, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer1.mObserverCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer1.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION); - TestObserver observer2; + TestObserver observer2; Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer( filename, ImageDimensions(), @@ -508,20 +507,20 @@ int UtcTextureManagerCachingForDifferentLoadingType(void) true, preMultiply); - DALI_TEST_EQUALS( observer2.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer2.mObserverCalled, false, TEST_LOCATION ); + DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer2.mObserverCalled, false, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION ); + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( observer2.mLoaded, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer2.mObserverCalled, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer2.mCompleteType, TestObserver::CompleteType::LOAD_COMPLETE, TEST_LOCATION ); + DALI_TEST_EQUALS(observer2.mLoaded, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer2.mObserverCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer2.mCompleteType, TestObserver::CompleteType::LOAD_COMPLETE, TEST_LOCATION); END_TEST; } @@ -529,28 +528,28 @@ int UtcTextureManagerCachingForDifferentLoadingType(void) int UtcTextureManagerUseInvalidMask(void) { ToolkitTestApplication application; - tet_infoline( "UtcTextureManagerUseInvalidMask" ); + tet_infoline("UtcTextureManagerUseInvalidMask"); TextureManager textureManager; // Create new texture manager - TestObserver observer; - std::string filename( TEST_IMAGE_FILE_NAME ); - std::string maskname(""); + TestObserver observer; + 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->mAlphaMaskUrl = maskname; + maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID; + maskInfo->mCropToMask = true; maskInfo->mContentScaleFactor = 1.0f; - auto textureId( TextureManager::INVALID_TEXTURE_ID ); - Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f ); - Dali::ImageDimensions atlasRectSize( 0,0 ); - bool synchronousLoading(false); - bool atlasingStatus(false); - bool loadingStatus(false); - auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - ImageAtlasManagerPtr atlasManager = nullptr; + auto textureId(TextureManager::INVALID_TEXTURE_ID); + Vector4 atlasRect(0.f, 0.f, 1.f, 1.f); + Dali::ImageDimensions atlasRectSize(0, 0); + bool synchronousLoading(false); + bool atlasingStatus(false); + bool loadingStatus(false); + auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + ImageAtlasManagerPtr atlasManager = nullptr; Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr; textureManager.LoadTexture( @@ -572,23 +571,22 @@ int UtcTextureManagerUseInvalidMask(void) atlasManager, true, TextureManager::ReloadPolicy::CACHED, - preMultiply - ); + preMultiply); - DALI_TEST_EQUALS( observer.mLoaded, false, TEST_LOCATION ); - DALI_TEST_EQUALS( observer.mObserverCalled, false, TEST_LOCATION ); + DALI_TEST_EQUALS(observer.mLoaded, false, TEST_LOCATION); + DALI_TEST_EQUALS(observer.mObserverCalled, false, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION ); + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS( observer.mLoaded, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer.mObserverCalled, true, TEST_LOCATION ); - DALI_TEST_EQUALS( observer.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION ); + DALI_TEST_EQUALS(observer.mLoaded, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer.mObserverCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(observer.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION); END_TEST; } @@ -596,31 +594,31 @@ int UtcTextureManagerUseInvalidMask(void) int UtcTextureManagerSynchronousLoadingFail(void) { ToolkitTestApplication application; - tet_infoline( "UtcTextureManagerSynchronousLoadingFail" ); + tet_infoline("UtcTextureManagerSynchronousLoadingFail"); TextureManager textureManager; // Create new texture manager - std::string maskname(""); + 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->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; + 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( + TextureSet textureSet = textureManager.LoadTexture( filename, ImageDimensions(), FittingMode::SCALE_TO_FILL, @@ -639,11 +637,10 @@ int UtcTextureManagerSynchronousLoadingFail(void) atlasManager, true, TextureManager::ReloadPolicy::CACHED, - preMultiply - ); + preMultiply); DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION); - DALI_TEST_CHECK(!textureSet); // texture loading fail. + DALI_TEST_CHECK(!textureSet); // texture loading fail. DALI_TEST_CHECK(textureId == TextureManager::INVALID_TEXTURE_ID); // invalid texture id is returned. END_TEST; @@ -652,32 +649,32 @@ int UtcTextureManagerSynchronousLoadingFail(void) int UtcTextureManagerCachingSynchronousLoading(void) { ToolkitTestApplication application; - tet_infoline( "UtcTextureManagerCachingSynchronousLoading" ); + tet_infoline("UtcTextureManagerCachingSynchronousLoading"); TextureManager textureManager; // Create new texture manager - std::string filename( TEST_IMAGE_FILE_NAME ); + std::string filename(TEST_IMAGE_FILE_NAME); - std::string maskname(""); + 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->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; + 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( + auto textureId(TextureManager::INVALID_TEXTURE_ID); + TextureSet textureSet = textureManager.LoadTexture( filename, ImageDimensions(), FittingMode::SCALE_TO_FILL, @@ -696,21 +693,19 @@ int UtcTextureManagerCachingSynchronousLoading(void) atlasManager, true, TextureManager::ReloadPolicy::CACHED, - preMultiply - ); + preMultiply); DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION); - DALI_TEST_CHECK(textureSet); // texture is loaded. + 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; + auto asyncTextureId(TextureManager::INVALID_TEXTURE_ID); + loadingStatus = false; TextureSet asyncTextureSet = textureManager.LoadTexture( filename, ImageDimensions(), @@ -730,12 +725,11 @@ int UtcTextureManagerCachingSynchronousLoading(void) atlasManager, true, TextureManager::ReloadPolicy::CACHED, - preMultiply - ); + preMultiply); DALI_TEST_EQUALS(asyncTextureId, textureId, TEST_LOCATION); // texture is loaded. DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION); - DALI_TEST_CHECK(asyncTextureSet); // Cached texture. + DALI_TEST_CHECK(asyncTextureSet); // Cached texture. // observer is directly called because textureSet is retrieved by cache. DALI_TEST_EQUALS(asyncObserver.mLoaded, true, TEST_LOCATION); @@ -747,32 +741,32 @@ int UtcTextureManagerCachingSynchronousLoading(void) int UtcTextureManagerAsyncSyncAsync(void) { ToolkitTestApplication application; - tet_infoline( "UtcTextureManagerAsyncSyncAsync" ); + tet_infoline("UtcTextureManagerAsyncSyncAsync"); TextureManager textureManager; // Create new texture manager - std::string filename( TEST_IMAGE_FILE_NAME ); + std::string filename(TEST_IMAGE_FILE_NAME); - std::string maskname(""); + 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->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; + 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( + auto asyncTextureId1(TextureManager::INVALID_TEXTURE_ID); + bool asyncLoadingStatus1 = false; + TextureSet asyncTextureSet1 = textureManager.LoadTexture( filename, ImageDimensions(), FittingMode::SCALE_TO_FILL, @@ -791,22 +785,20 @@ int UtcTextureManagerAsyncSyncAsync(void) atlasManager, true, TextureManager::ReloadPolicy::CACHED, - preMultiply - ); + preMultiply); DALI_TEST_EQUALS(asyncLoadingStatus1, true, TEST_LOCATION); // texture is loading now. - DALI_TEST_CHECK(!asyncTextureSet1); // texture is not loaded yet. + 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( + auto textureId(TextureManager::INVALID_TEXTURE_ID); + bool syncLoadingStatus = false; + TextureSet syncTextureSet = textureManager.LoadTexture( filename, ImageDimensions(), FittingMode::SCALE_TO_FILL, @@ -825,12 +817,11 @@ int UtcTextureManagerAsyncSyncAsync(void) atlasManager, true, TextureManager::ReloadPolicy::CACHED, - preMultiply - ); + 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. + 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); @@ -840,13 +831,11 @@ int UtcTextureManagerAsyncSyncAsync(void) 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( + auto asyncTextureId2(TextureManager::INVALID_TEXTURE_ID); + bool asyncLoadingStatus2 = false; + TextureSet asyncTextureSet2 = textureManager.LoadTexture( filename, ImageDimensions(), FittingMode::SCALE_TO_FILL, @@ -865,28 +854,27 @@ int UtcTextureManagerAsyncSyncAsync(void) atlasManager, true, TextureManager::ReloadPolicy::CACHED, - preMultiply - ); + 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. + 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.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(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. + 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); diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index c65b61e..4ce57ab 100644 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -13,6 +13,10 @@ SET( toolkit_src_files ${toolkit_src_dir}/builder/style.cpp ${toolkit_src_dir}/builder/tree-node-manipulator.cpp ${toolkit_src_dir}/builder/replacement.cpp + ${toolkit_src_dir}/texture-manager/texture-async-loading-helper.cpp + ${toolkit_src_dir}/texture-manager/texture-cache-manager.cpp + ${toolkit_src_dir}/texture-manager/texture-manager-impl.cpp + ${toolkit_src_dir}/texture-manager/texture-upload-observer.cpp ${toolkit_src_dir}/visuals/animated-image/animated-image-visual.cpp ${toolkit_src_dir}/visuals/animated-image/image-cache.cpp ${toolkit_src_dir}/visuals/animated-image/fixed-image-cache.cpp @@ -42,8 +46,6 @@ SET( toolkit_src_files ${toolkit_src_dir}/visuals/svg/svg-visual.cpp ${toolkit_src_dir}/visuals/text/text-visual.cpp ${toolkit_src_dir}/visuals/transition-data-impl.cpp - ${toolkit_src_dir}/visuals/texture-manager-impl.cpp - ${toolkit_src_dir}/visuals/texture-upload-observer.cpp ${toolkit_src_dir}/visuals/image-visual-shader-factory.cpp ${toolkit_src_dir}/visuals/visual-base-data-impl.cpp ${toolkit_src_dir}/visuals/visual-base-impl.cpp diff --git a/dali-toolkit/internal/image-loader/image-url-impl.cpp b/dali-toolkit/internal/image-loader/image-url-impl.cpp index 52ba6c4..0742d3c 100644 --- a/dali-toolkit/internal/image-loader/image-url-impl.cpp +++ b/dali-toolkit/internal/image-loader/image-url-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,8 @@ // INTERNAL INCLUDES #include +#include #include -#include #include namespace Dali @@ -42,7 +42,7 @@ ImageUrl::ImageUrl(const EncodedImageBuffer& encodedImageBuffer) if(visualFactory) { auto& textureManager = GetImplementation(visualFactory).GetTextureManager(); - mUrl = textureManager.AddExternalEncodedImageBuffer(encodedImageBuffer); + mUrl = textureManager.AddExternalEncodedImageBuffer(encodedImageBuffer); } } diff --git a/dali-toolkit/internal/texture-manager/texture-async-loading-helper.cpp b/dali-toolkit/internal/texture-manager/texture-async-loading-helper.cpp new file mode 100644 index 0000000..1eee5bc --- /dev/null +++ b/dali-toolkit/internal/texture-manager/texture-async-loading-helper.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include "texture-async-loading-helper.h" + +// EXTERNAL HEADERS +#include + +// INTERNAL HEADERS +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +#ifdef DEBUG_ENABLED +extern Debug::Filter* gTextureManagerLogFilter; ///< Define at texture-manager-impl.cpp +#endif + +TextureAsyncLoadingHelper::TextureAsyncLoadingHelper(TextureManager& textureManager) +: TextureAsyncLoadingHelper(Toolkit::AsyncImageLoader::New(), textureManager, AsyncLoadingInfoContainerType()) +{ +} + +void TextureAsyncLoadingHelper::LoadAnimatedImage(const TextureManager::TextureId& textureId, + Dali::AnimatedImageLoading animatedImageLoading, + const std::uint32_t& frameIndex) +{ + mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId)); + auto id = GetImplementation(mLoader).LoadAnimatedImage(animatedImageLoading, frameIndex); + mLoadingInfoContainer.back().loadId = id; +} + +void TextureAsyncLoadingHelper::Load(const TextureManager::TextureId& textureId, + const VisualUrl& url, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const bool& orientationCorrection, + const DevelAsyncImageLoader::PreMultiplyOnLoad& preMultiplyOnLoad) +{ + mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId)); + if(DALI_UNLIKELY(url.IsBufferResource())) + { + auto id = GetImplementation(mLoader).LoadEncodedImageBuffer(mTextureManager.GetEncodedImageBuffer(url.GetUrl()), desiredSize, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad); + mLoadingInfoContainer.back().loadId = id; + } + else + { + auto id = GetImplementation(mLoader).Load(url, desiredSize, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad); + mLoadingInfoContainer.back().loadId = id; + } +} + +void TextureAsyncLoadingHelper::ApplyMask(const TextureManager::TextureId& textureId, + Devel::PixelBuffer pixelBuffer, + Devel::PixelBuffer maskPixelBuffer, + const float& contentScale, + const bool& cropToMask, + const DevelAsyncImageLoader::PreMultiplyOnLoad& preMultiplyOnLoad) +{ + mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId)); + auto id = GetImplementation(mLoader).ApplyMask(pixelBuffer, maskPixelBuffer, contentScale, cropToMask, preMultiplyOnLoad); + mLoadingInfoContainer.back().loadId = id; +} + +TextureAsyncLoadingHelper::TextureAsyncLoadingHelper(TextureAsyncLoadingHelper&& rhs) +: TextureAsyncLoadingHelper(rhs.mLoader, rhs.mTextureManager, std::move(rhs.mLoadingInfoContainer)) +{ +} + +TextureAsyncLoadingHelper::TextureAsyncLoadingHelper( + Toolkit::AsyncImageLoader loader, + TextureManager& textureManager, + AsyncLoadingInfoContainerType&& loadingInfoContainer) +: mLoader(loader), + mTextureManager(textureManager), + mLoadingInfoContainer(std::move(loadingInfoContainer)) +{ + DevelAsyncImageLoader::PixelBufferLoadedSignal(mLoader).Connect( + this, &TextureAsyncLoadingHelper::AsyncLoadComplete); +} + +void TextureAsyncLoadingHelper::AsyncLoadComplete(uint32_t id, + Devel::PixelBuffer pixelBuffer) +{ + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureAsyncLoadingHelper::AsyncLoadComplete( loadId :%d )\n", id); + if(mLoadingInfoContainer.size() >= 1u) + { + AsyncLoadingInfo loadingInfo = mLoadingInfoContainer.front(); + + // We can assume that First Loading task comes First. + if(loadingInfo.loadId == id) + { + // Call TextureManager::AsyncLoadComplete + mTextureManager.AsyncLoadComplete(loadingInfo.textureId, pixelBuffer); + } + + mLoadingInfoContainer.pop_front(); + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/texture-manager/texture-async-loading-helper.h b/dali-toolkit/internal/texture-manager/texture-async-loading-helper.h new file mode 100644 index 0000000..4bfc961 --- /dev/null +++ b/dali-toolkit/internal/texture-manager/texture-async-loading-helper.h @@ -0,0 +1,145 @@ +#ifndef DALI_TOOLKIT_TEXTURE_ASYNC_LOADING_HELPER_H +#define DALI_TOOLKIT_TEXTURE_ASYNC_LOADING_HELPER_H + +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +/** + * @brief Helper class to keep the relation between AsyncImageLoader and corresponding LoadingInfo container + */ +class TextureAsyncLoadingHelper : public ConnectionTracker +{ + /** + * Struct to hold information about a requested Async load. + * This is used to look up a TextureManager::TextureId from the returned AsyncLoad Id. + */ + struct AsyncLoadingInfo + { + AsyncLoadingInfo(TextureManager::TextureId textureId) + : textureId(textureId), + loadId(0) + { + } + + TextureManager::TextureId textureId; ///< The external Texture Id assigned to this load + std::uint32_t loadId; ///< The load Id used by the async loader to reference this load + }; + +public: + /** + * @brief Create an TextureAsyncLoadingHelper. + * @param[in] textureManager Reference to the texture manager + */ + TextureAsyncLoadingHelper(TextureManager& textureManager); + + /** + * @brief Load a new frame of animated image + * @param[in] textureId TextureId to reference the texture that will be loaded + * @param[in] animatedImageLoading The AnimatedImageLoading to load animated image + * @param[in] frameIndex The frame index of a frame to be loaded frame + */ + void LoadAnimatedImage(const TextureManager::TextureId& textureId, + Dali::AnimatedImageLoading animatedImageLoading, + const std::uint32_t& frameIndex); + + /** + * @brief Load a new texture. + * @param[in] textureId TextureId to reference the texture that will be loaded + * @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 + * @param[in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha or if the image need to be applied alpha mask. + */ + void Load(const TextureManager::TextureId& textureId, + const VisualUrl& url, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const bool& orientationCorrection, + const DevelAsyncImageLoader::PreMultiplyOnLoad& preMultiplyOnLoad); + + /** + * @brief Apply mask + * @param [in] textureId of the texture + * @param [in] pixelBuffer of the to be masked image + * @param [in] maskPixelBuffer of the mask image + * @param [in] contentScale The factor to scale the content + * @param [in] cropToMask Whether to crop the content to the mask size + * @param [in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha. + */ + void ApplyMask(const TextureManager::TextureId& textureId, + Devel::PixelBuffer pixelBuffer, + Devel::PixelBuffer maskPixelBuffer, + const float& contentScale, + const bool& cropToMask, + const DevelAsyncImageLoader::PreMultiplyOnLoad& preMultiplyOnLoad); + +public: + TextureAsyncLoadingHelper(const TextureAsyncLoadingHelper&) = delete; + TextureAsyncLoadingHelper& operator=(const TextureAsyncLoadingHelper&) = delete; + + TextureAsyncLoadingHelper(TextureAsyncLoadingHelper&& rhs); + TextureAsyncLoadingHelper& operator=(TextureAsyncLoadingHelper&& rhs) = delete; + +private: // Private typedefs: + typedef std::deque AsyncLoadingInfoContainerType; ///< The container type used to manage Asynchronous loads in progress + +private: + /** + * @brief Main constructor that used by all other constructors + */ + TextureAsyncLoadingHelper(Toolkit::AsyncImageLoader loader, + TextureManager& textureManager, + AsyncLoadingInfoContainerType&& loadingInfoContainer); + + /** + * @brief Callback to be called when texture loading is complete, it passes the pixel buffer on to texture manager. + * @param[in] id Loader id + * @param[in] pixelBuffer Image data + */ + void AsyncLoadComplete(std::uint32_t id, Devel::PixelBuffer pixelBuffer); + +private: // Member Variables: + Toolkit::AsyncImageLoader mLoader; + TextureManager& mTextureManager; + AsyncLoadingInfoContainerType mLoadingInfoContainer; +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // DALI_TOOLKIT_TEXTURE_ASYNC_LOADING_HELPER_H diff --git a/dali-toolkit/internal/texture-manager/texture-cache-manager.cpp b/dali-toolkit/internal/texture-manager/texture-cache-manager.cpp new file mode 100644 index 0000000..3fe5105 --- /dev/null +++ b/dali-toolkit/internal/texture-manager/texture-cache-manager.cpp @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include "texture-cache-manager.h" + +// EXTERNAL HEADERS +#include +#include + +// INTERNAL HEADERS + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +#ifdef DEBUG_ENABLED +extern Debug::Filter* gTextureManagerLogFilter; ///< Define at texture-manager-impl.cpp + +// clang-format off +#define GET_LOAD_STATE_STRING(loadState) \ + loadState == TextureManagerType::LoadState::NOT_STARTED ? "NOT_STARTED" : \ + loadState == TextureManagerType::LoadState::LOADING ? "LOADING" : \ + loadState == TextureManagerType::LoadState::LOAD_FINISHED ? "LOAD_FINISHED" : \ + loadState == TextureManagerType::LoadState::WAITING_FOR_MASK ? "WAITING_FOR_MASK" : \ + loadState == TextureManagerType::LoadState::MASK_APPLYING ? "MASK_APPLYING" : \ + loadState == TextureManagerType::LoadState::MASK_APPLIED ? "MASK_APPLIED" : \ + loadState == TextureManagerType::LoadState::UPLOADED ? "UPLOADED" : \ + loadState == TextureManagerType::LoadState::CANCELLED ? "CANCELLED" : \ + loadState == TextureManagerType::LoadState::LOAD_FAILED ? "LOAD_FAILED" : \ + "Unknown" +// clang-format on +#endif +namespace +{ +} // Anonymous namespace + +TextureCacheManager::TextureCacheManager() +: mCurrentTextureId(0) +{ +} + +TextureCacheManager::~TextureCacheManager() +{ +} + +VisualUrl TextureCacheManager::GetVisualUrl(const TextureCacheManager::TextureId& textureId) +{ + VisualUrl visualUrl(""); + TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId); + + if(cacheIndex != INVALID_CACHE_INDEX) + { + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached texture id=%d, textureId=%d\n", cacheIndex, textureId); + + TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); + visualUrl = cachedTextureInfo.url; + } + return visualUrl; +} + +TextureCacheManager::LoadState TextureCacheManager::GetTextureState(const TextureCacheManager::TextureId& textureId) +{ + LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED; + + TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId); + if(cacheIndex != INVALID_CACHE_INDEX) + { + TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); + loadState = cachedTextureInfo.loadState; + } + else + { + for(auto&& elem : mExternalTextures) + { + if(elem.textureId == textureId) + { + loadState = LoadState::UPLOADED; + break; + } + } + } + return loadState; +} + +TextureCacheManager::LoadState TextureCacheManager::GetTextureStateInternal(const TextureCacheManager::TextureId& textureId) +{ + LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED; + + TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId); + if(cacheIndex != INVALID_CACHE_INDEX) + { + TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); + loadState = cachedTextureInfo.loadState; + } + + return loadState; +} + +TextureSet TextureCacheManager::GetTextureSet(const TextureCacheManager::TextureId& textureId) +{ + TextureSet textureSet; // empty handle + + TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId); + if(cacheIndex != INVALID_CACHE_INDEX) + { + TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); + textureSet = cachedTextureInfo.textureSet; + } + else + { + for(auto&& elem : mExternalTextures) + { + if(elem.textureId == textureId) + { + textureSet = elem.textureSet; + break; + } + } + } + return textureSet; +} + +TextureSet TextureCacheManager::GetExternalTextureSet(const TextureCacheManager::TextureId& textureId) +{ + TextureSet textureSet; // empty handle + for(auto&& elem : mExternalTextures) + { + if(elem.textureId == textureId) + { + textureSet = elem.textureSet; + break; + } + } + return textureSet; +} + +EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId& textureId) +{ + EncodedImageBuffer encodedImageBuffer; // empty handle + for(auto&& elem : mEncodedBufferTextures) + { + if(elem.textureId == textureId) + { + encodedImageBuffer = elem.encodedImageBuffer; + break; + } + } + return encodedImageBuffer; +} + +EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const std::string& url) +{ + EncodedImageBuffer encodedImageBuffer; // empty handle + if(url.size() > 0 && VisualUrl::BUFFER == VisualUrl::GetProtocolType(url)) + { + std::string location = VisualUrl::GetLocation(url); + if(location.size() > 0u) + { + TextureId targetId = std::stoi(location); + return GetEncodedImageBuffer(targetId); + } + } + return encodedImageBuffer; +} + +std::string TextureCacheManager::AddExternalTexture(const TextureSet& textureSet) +{ + TextureCacheManager::ExternalTextureInfo info(GenerateUniqueTextureId(), textureSet); + mExternalTextures.emplace_back(info); + return VisualUrl::CreateTextureUrl(std::to_string(info.textureId)); +} + +std::string TextureCacheManager::AddExternalEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer) +{ + // Duplication check + for(auto&& elem : mEncodedBufferTextures) + { + if(elem.encodedImageBuffer == encodedImageBuffer) + { + // If same buffer added, increase reference count and return. + elem.referenceCount++; + return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId)); + } + } + TextureCacheManager::EncodedBufferTextureInfo info(GenerateUniqueTextureId(), encodedImageBuffer); + mEncodedBufferTextures.emplace_back(info); + return VisualUrl::CreateBufferUrl(std::to_string(info.textureId)); +} + +TextureSet TextureCacheManager::RemoveExternalTexture(const std::string& url) +{ + if(url.size() > 0u) + { + if(VisualUrl::TEXTURE == VisualUrl::GetProtocolType(url)) + { + // get the location from the Url + std::string location = VisualUrl::GetLocation(url); + if(location.size() > 0u) + { + TextureId id = std::stoi(location); + const auto end = mExternalTextures.end(); + for(auto iter = mExternalTextures.begin(); iter != end; ++iter) + { + if(iter->textureId == id) + { + auto textureSet = iter->textureSet; + if(--(iter->referenceCount) <= 0) + { + mExternalTextures.erase(iter); + } + return textureSet; + } + } + } + } + } + return TextureSet(); +} + +EncodedImageBuffer TextureCacheManager::RemoveExternalEncodedImageBuffer(const std::string& url) +{ + if(url.size() > 0u) + { + if(VisualUrl::BUFFER == VisualUrl::GetProtocolType(url)) + { + // get the location from the Url + std::string location = VisualUrl::GetLocation(url); + if(location.size() > 0u) + { + TextureId id = std::stoi(location); + const auto end = mEncodedBufferTextures.end(); + for(auto iter = mEncodedBufferTextures.begin(); iter != end; ++iter) + { + if(iter->textureId == id) + { + auto encodedImageBuffer = iter->encodedImageBuffer; + if(--(iter->referenceCount) <= 0) + { + mEncodedBufferTextures.erase(iter); + } + return encodedImageBuffer; + } + } + } + } + } + return EncodedImageBuffer(); +} + +void TextureCacheManager::UseExternalResource(const VisualUrl& url) +{ + if(VisualUrl::TEXTURE == url.GetProtocolType()) + { + std::string location = url.GetLocation(); + if(location.size() > 0u) + { + TextureId id = std::stoi(location); + for(auto&& elem : mExternalTextures) + { + if(elem.textureId == id) + { + elem.referenceCount++; + return; + } + } + } + } + else if(VisualUrl::BUFFER == url.GetProtocolType()) + { + std::string location = url.GetLocation(); + if(location.size() > 0u) + { + TextureId id = std::stoi(location); + for(auto&& elem : mEncodedBufferTextures) + { + if(elem.textureId == id) + { + elem.referenceCount++; + return; + } + } + } + } +} + +TextureCacheManager::TextureId TextureCacheManager::GenerateUniqueTextureId() +{ + return mCurrentTextureId++; +} + +TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromId(const TextureCacheManager::TextureId& textureId) +{ + const TextureCacheIndex size = static_cast(mTextureInfoContainer.size()); + + for(TextureCacheIndex i = 0; i < size; ++i) + { + if(mTextureInfoContainer[i].textureId == textureId) + { + return i; + } + } + + return INVALID_CACHE_INDEX; +} + +TextureCacheManager::TextureHash TextureCacheManager::GenerateHash( + const std::string& url, + const Dali::ImageDimensions& size, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const TextureCacheManager::UseAtlas& useAtlas, + const TextureCacheManager::TextureId& maskTextureId) +{ + std::string hashTarget(url); + const size_t urlLength = hashTarget.length(); + const uint16_t width = size.GetWidth(); + const uint16_t height = size.GetWidth(); + + // If either the width or height has been specified, include the resizing options in the hash + if(width != 0 || height != 0) + { + // We are appending 5 bytes to the URL to form the hash input. + hashTarget.resize(urlLength + 5u); + char* hashTargetPtr = &(hashTarget[urlLength]); + + // Pack the width and height (4 bytes total). + *hashTargetPtr++ = size.GetWidth() & 0xff; + *hashTargetPtr++ = (size.GetWidth() >> 8u) & 0xff; + *hashTargetPtr++ = size.GetHeight() & 0xff; + *hashTargetPtr++ = (size.GetHeight() >> 8u) & 0xff; + + // Bit-pack the FittingMode, SamplingMode and atlasing. + // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit + *hashTargetPtr = (fittingMode << 4u) | (samplingMode << 1) | (useAtlas == UseAtlas::USE_ATLAS ? 1 : 0); + } + else + { + // We are not including sizing information, but we still need an extra byte for atlasing. + hashTarget.resize(urlLength + 1u); + + // Add the atlasing to the hash input. + switch(useAtlas) + { + case UseAtlas::NO_ATLAS: + { + hashTarget[urlLength] = 'f'; + break; + } + case UseAtlas::USE_ATLAS: + { + hashTarget[urlLength] = 't'; + break; + } + } + } + + if(maskTextureId != INVALID_TEXTURE_ID) + { + auto textureIdIndex = hashTarget.length(); + hashTarget.resize(hashTarget.length() + sizeof(TextureId)); + unsigned char* hashTargetPtr = reinterpret_cast(&(hashTarget[textureIdIndex])); + + // Append the texture id to the end of the URL byte by byte: + // (to avoid SIGBUS / alignment issues) + TextureId saltedMaskTextureId = maskTextureId; + for(size_t byteIter = 0; byteIter < sizeof(TextureId); ++byteIter) + { + *hashTargetPtr++ = saltedMaskTextureId & 0xff; + saltedMaskTextureId >>= 8u; + } + } + + return Dali::CalculateHash(hashTarget); +} + +TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture( + const TextureCacheManager::TextureHash& hash, + const std::string& url, + const Dali::ImageDimensions& size, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const TextureCacheManager::UseAtlas& useAtlas, + const TextureCacheManager::TextureId& maskTextureId, + const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad) +{ + // Default to an invalid ID, in case we do not find a match. + TextureCacheIndex cacheIndex = INVALID_CACHE_INDEX; + + // Iterate through our hashes to find a match. + const TextureCacheIndex count = static_cast(mTextureInfoContainer.size()); + for(TextureCacheIndex i = 0u; i < count; ++i) + { + if(mTextureInfoContainer[i].hash == hash) + { + // We have a match, now we check all the original parameters in case of a hash collision. + TextureInfo& textureInfo(mTextureInfoContainer[i]); + + if((url == textureInfo.url.GetUrl()) && + (useAtlas == textureInfo.useAtlas) && + (maskTextureId == textureInfo.maskTextureId) && + (size == textureInfo.desiredSize) && + ((size.GetWidth() == 0 && size.GetHeight() == 0) || + (fittingMode == textureInfo.fittingMode && + samplingMode == textureInfo.samplingMode))) + { + // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different. + // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false. + if((preMultiplyOnLoad == MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad) || (preMultiplyOnLoad == MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied)) + { + // The found Texture is a match. + cacheIndex = i; + break; + } + } + } + } + + return cacheIndex; +} + +TextureCacheManager::TextureCacheIndex TextureCacheManager::AppendCache(const TextureCacheManager::TextureInfo& textureInfo) +{ + TextureCacheIndex cacheIndex = static_cast(mTextureInfoContainer.size()); + mTextureInfoContainer.emplace_back(textureInfo); + return cacheIndex; +} + +void TextureCacheManager::RemoveCache(const TextureCacheManager::TextureId& textureId) +{ + TextureCacheIndex textureInfoIndex = GetCacheIndexFromId(textureId); + + if(textureInfoIndex != INVALID_CACHE_INDEX) + { + TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex]); + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::Remove(textureId:%d) url:%s\n cacheIdx:%d loadState:%s reference count = %d\n", textureId, textureInfo.url.GetUrl().c_str(), textureInfoIndex, GET_LOAD_STATE_STRING(textureInfo.loadState), textureInfo.referenceCount); + + // Decrement the reference count and check if this is the last user of this Texture. + if(--textureInfo.referenceCount <= 0) + { + // This is the last remove for this Texture. + textureInfo.referenceCount = 0; + bool removeTextureInfo = false; + + // If loaded, we can remove the TextureInfo and the Atlas (if atlased). + if(textureInfo.loadState == LoadState::UPLOADED) + { + if(textureInfo.atlas) + { + textureInfo.atlas.Remove(textureInfo.atlasRect); + } + removeTextureInfo = true; + } + else if(textureInfo.loadState == LoadState::LOADING) + { + // We mark the textureInfo for removal. + // Once the load has completed, this method will be called again. + textureInfo.loadState = LoadState::CANCELLED; + } + else + { + // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data. + removeTextureInfo = true; + } + + // If the state allows us to remove the TextureInfo data, we do so. + if(removeTextureInfo) + { + // If url location is BUFFER, decrease reference count of EncodedImageBuffer. + if(textureInfo.url.IsBufferResource()) + { + RemoveExternalEncodedImageBuffer(textureInfo.url.GetUrl()); + } + // Permanently remove the textureInfo struct. + mTextureInfoContainer.erase(mTextureInfoContainer.begin() + textureInfoIndex); + } + } + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/texture-manager/texture-cache-manager.h b/dali-toolkit/internal/texture-manager/texture-cache-manager.h new file mode 100644 index 0000000..5e82d47 --- /dev/null +++ b/dali-toolkit/internal/texture-manager/texture-cache-manager.h @@ -0,0 +1,320 @@ +#ifndef DALI_TOOLKIT_TEXTURE_CACHE_MANAGER_H +#define DALI_TOOLKIT_TEXTURE_CACHE_MANAGER_H + +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +/** + * @brief The contain and managing cached textures. + * Each Texture hold there TextureId. These TextureId can be used outside of TextureManager. + * Internally, each cached texture can be accessed by TextureCacheIndex. + * You can Convert TextureId into TextureCacheIndex by this class. + * + * Also, You can store external TextureSet or EncodedImageBuffer here. + */ +class TextureCacheManager +{ +public: + // Copy enum and types and const values that TextureCacheManager will use. + using TextureId = TextureManagerType::TextureId; + using TextureCacheIndex = TextureManagerType::TextureCacheIndex; + using TextureHash = TextureManagerType::TextureHash; + + static constexpr TextureId INVALID_TEXTURE_ID = TextureManagerType::INVALID_TEXTURE_ID; + static constexpr TextureCacheIndex INVALID_CACHE_INDEX = TextureManagerType::INVALID_CACHE_INDEX; + + using UseAtlas = TextureManagerType::UseAtlas; + using StorageType = TextureManagerType::StorageType; + using LoadType = TextureManagerType::LoadType; + using LoadState = TextureManagerType::LoadState; + using ReloadPolicy = TextureManagerType::ReloadPolicy; + using MultiplyOnLoad = TextureManagerType::MultiplyOnLoad; + using TextureInfo = TextureManagerType::TextureInfo; + +public: + /** + * Constructor. + */ + TextureCacheManager(); + + /** + * Destructor. + */ + ~TextureCacheManager(); + +public: + // TextureCacheManager Main API: + + /** + * @brief Get the visualUrl associated with the texture id. + * @param[in] textureId The texture Id to get + * @return The visual Url associated with the texture id. + */ + VisualUrl GetVisualUrl(const TextureCacheManager::TextureId& textureId); + + /** + * @brief Get the current state of a texture + * @param[in] textureId The texture id to query + * @return The loading state if the texture is valid, or NOT_STARTED if the textureId + * is not valid. + */ + TextureCacheManager::LoadState GetTextureState(const TextureCacheManager::TextureId& textureId); + + /** + * @brief Get the current state of a texture + * @param[in] textureId The texture id to query + * @return The loading state if the texture is valid, or NOT_STARTED if the textureId + * is not valid. + */ + TextureCacheManager::LoadState GetTextureStateInternal(const TextureCacheManager::TextureId& textureId); + + /** + * @brief Get the associated texture set if the texture id is valid + * @param[in] textureId The texture Id to look up + * @return the associated texture set, or an empty handle if textureId is not valid + */ + TextureSet GetTextureSet(const TextureCacheManager::TextureId& textureId); + + /** + * @brief Get the external texture set if the texture id is valid + * @param[in] textureId The texture Id to look up + * @return the external texture set, or an empty handle if textureId is not valid + */ + TextureSet GetExternalTextureSet(const TextureCacheManager::TextureId& textureId); + + /** + * @brief Get the encoded image buffer + * @param[in] textureId The textureId to look up + * @return the encoded image buffer, or an empty handle if textureId is not valid + */ + EncodedImageBuffer GetEncodedImageBuffer(const TextureCacheManager::TextureId& textureId); + + /** + * @brief Get the encoded image buffer by VisualUrl + * @param[in] url The url to look up + * @return the encoded image buffer, or an empty handle if url is not buffer resource or buffer is not valid + */ + EncodedImageBuffer GetEncodedImageBuffer(const std::string& url); + + /** + * Adds an external texture to the texture manager + * @param[in] texture The texture to add + * @return string containing the URL for the texture + */ + std::string AddExternalTexture(const TextureSet& texture); + + /** + * Adds an external encoded image buffer to the texture manager + * @param[in] encodedImageBuffer The image buffer to add + * @return string containing the URL for the texture + */ + std::string AddExternalEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer); + + /** + * Removes an external texture from texture manager + * @param[in] url The string containing the texture to remove + * @return handle to the texture + */ + TextureSet RemoveExternalTexture(const std::string& url); + + /** + * Removes an external encoded image buffer from texture manager + * @param[in] url The string containing the encoded image buffer to remove + * @return handle to the encoded image buffer + */ + EncodedImageBuffer RemoveExternalEncodedImageBuffer(const std::string& url); + + /** + * @brief Notify that external textures or external encoded image buffers are used. + * @param[in] url The URL of the texture to use. + */ + void UseExternalResource(const VisualUrl& url); + +public: + // To Generate / Get / Remove TextureId. + + /** + * @brief Generates a new, unique TextureId + * @return A unique TextureId + */ + TextureCacheManager::TextureId GenerateUniqueTextureId(); + + /** + * @brief Used to lookup an index into the TextureInfoContainer from a TextureId + * @param[in] textureId The TextureId to look up + * @return The cache index + */ + TextureCacheManager::TextureCacheIndex GetCacheIndexFromId(const TextureCacheManager::TextureId& textureId); + + /** + * @brief Generates a hash for caching based on the input parameters. + * Only applies size, fitting mode andsampling mode if the size is specified. + * Only applies maskTextureId if it isn't INVALID_TEXTURE_ID + * Always applies useAtlas. + * @param[in] url The URL of the image to load + * @param[in] size The image size + * @param[in] fittingMode The FittingMode to use + * @param[in] samplingMode The SamplingMode to use + * @param[in] useAtlas True if atlased + * @param[in] maskTextureId The masking texture id (or INVALID_TEXTURE_ID) + * @return A hash of the provided data for caching. + */ + TextureCacheManager::TextureHash GenerateHash( + const std::string& url, + const Dali::ImageDimensions& size, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const TextureCacheManager::UseAtlas& useAtlas, + const TextureCacheManager::TextureId& maskTextureId); + + /** + * @brief Looks up a cached texture by its hash. + * If found, the given parameters are used to check there is no hash-collision. + * @param[in] hash The hash to look up + * @param[in] url The URL of the image to load + * @param[in] size The image size + * @param[in] fittingMode The FittingMode to use + * @param[in] samplingMode The SamplingMode to use + * @param[in] useAtlas True if atlased + * @param[in] maskTextureId Optional texture ID to use to mask this image + * @param[in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha. + * @return A TextureCacheId of a cached Texture if found. Or INVALID_CACHE_INDEX if not found. + */ + TextureCacheManager::TextureCacheIndex FindCachedTexture( + const TextureCacheManager::TextureHash& hash, + const std::string& url, + const Dali::ImageDimensions& size, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const TextureCacheManager::UseAtlas& useAtlas, + const TextureCacheManager::TextureId& maskTextureId, + const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad); + + /** + * @brief Append a Texture to the TextureCacheManager. + * @note This API doesn't check duplication of TextureId. + * + * @param[in] textureInfo TextureInfo that want to cache in container. + * @return Index of newly appended texture info. + */ + TextureCacheManager::TextureCacheIndex AppendCache(const TextureCacheManager::TextureInfo& textureInfo); + + /** + * @brief Remove a Texture from the TextureCacheManager. + * + * Textures are cached and therefore only the removal of the last + * occurrence of a Texture will cause its removal internally. + * + * @param[in] textureId The Id of the Texture to remove at Cache. + */ + void RemoveCache(const TextureCacheManager::TextureId& textureId); + +public: + /** + * @brief Get TextureInfo as TextureCacheIndex. + * @note This API doesn't consider external & encodedimagebuffer. + * @param[in] textureCacheIndex Index of cahced texture. + * @return TextureInfo as textureCacheIndex + */ + inline TextureCacheManager::TextureInfo& operator[](const TextureCacheManager::TextureCacheIndex& textureCacheIndex) noexcept + { + return mTextureInfoContainer[textureCacheIndex]; + } + + /** + * @brief The number of associated cached image + * @note This API doesn't consider external & encodedimagebuffer. + * @return The number of associated cached image + */ + inline std::size_t size() noexcept + { + return mTextureInfoContainer.size(); + } + +private: + // Private defined structs. + + struct ExternalTextureInfo + { + ExternalTextureInfo(const TextureCacheManager::TextureId& textureId, + const TextureSet& textureSet) + : textureId(textureId), + textureSet(textureSet), + referenceCount(1u) + { + } + TextureCacheManager::TextureId textureId; + TextureSet textureSet; + std::int16_t referenceCount; + }; + + struct EncodedBufferTextureInfo + { + EncodedBufferTextureInfo(const TextureCacheManager::TextureId& textureId, + const EncodedImageBuffer& encodedImageBuffer) + : textureId(textureId), + encodedImageBuffer(encodedImageBuffer), + referenceCount(1u) + { + } + TextureCacheManager::TextureId textureId; + EncodedImageBuffer encodedImageBuffer; + std::int16_t referenceCount; + }; + + typedef std::vector TextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures + typedef std::vector ExternalTextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures + typedef std::vector EncodedBufferTextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures + +private: + /** + * Deleted copy constructor. + */ + TextureCacheManager(const TextureCacheManager&) = delete; + + /** + * Deleted assignment operator. + */ + TextureCacheManager& operator=(const TextureCacheManager& rhs) = delete; + +private: // Member Variables: + TextureInfoContainerType mTextureInfoContainer{}; ///< Used to manage the life-cycle and caching of Textures + ExternalTextureInfoContainerType mExternalTextures{}; ///< Externally provided textures + EncodedBufferTextureInfoContainerType mEncodedBufferTextures{}; ///< Externally encoded buffer textures + TextureCacheManager::TextureId mCurrentTextureId; ///< The current value used for the unique Texture Id generation +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // DALI_TOOLKIT_TEXTURE_CACHE_MANAGER_H diff --git a/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp new file mode 100644 index 0000000..31c57c6 --- /dev/null +++ b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp @@ -0,0 +1,1108 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include "texture-manager-impl.h" + +// EXTERNAL HEADERS +#include +#include +#include +#include +#include + +// INTERNAL HEADERS +#include +#include +#include +#include + +namespace +{ +constexpr auto INITIAL_HASH_NUMBER = size_t{0u}; +constexpr auto DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS = size_t{4u}; +constexpr auto DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS = size_t{8u}; + +constexpr auto NUMBER_OF_LOCAL_LOADER_THREADS_ENV = "DALI_TEXTURE_LOCAL_THREADS"; +constexpr auto NUMBER_OF_REMOTE_LOADER_THREADS_ENV = "DALI_TEXTURE_REMOTE_THREADS"; + +size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue) +{ + using Dali::EnvironmentVariable::GetEnvironmentVariable; + auto numberString = GetEnvironmentVariable(environmentVariable); + auto numberOfThreads = numberString ? std::strtoul(numberString, nullptr, 10) : 0; + constexpr auto MAX_NUMBER_OF_THREADS = 100u; + DALI_ASSERT_DEBUG(numberOfThreads < MAX_NUMBER_OF_THREADS); + return (numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS) ? numberOfThreads : defaultValue; +} + +size_t GetNumberOfLocalLoaderThreads() +{ + return GetNumberOfThreads(NUMBER_OF_LOCAL_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS); +} + +size_t GetNumberOfRemoteLoaderThreads() +{ + return GetNumberOfThreads(NUMBER_OF_REMOTE_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS); +} + +} // namespace + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +#ifdef DEBUG_ENABLED +// Define logfilter Internal namespace level so other files (e.g. texture-cache-manager.cpp) can also use this filter. +Debug::Filter* gTextureManagerLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXTURE_MANAGER"); + +// clang-format off +#define GET_LOAD_STATE_STRING(loadState) \ + loadState == TextureManagerType::LoadState::NOT_STARTED ? "NOT_STARTED" : \ + loadState == TextureManagerType::LoadState::LOADING ? "LOADING" : \ + loadState == TextureManagerType::LoadState::LOAD_FINISHED ? "LOAD_FINISHED" : \ + loadState == TextureManagerType::LoadState::WAITING_FOR_MASK ? "WAITING_FOR_MASK" : \ + loadState == TextureManagerType::LoadState::MASK_APPLYING ? "MASK_APPLYING" : \ + loadState == TextureManagerType::LoadState::MASK_APPLIED ? "MASK_APPLIED" : \ + loadState == TextureManagerType::LoadState::UPLOADED ? "UPLOADED" : \ + loadState == TextureManagerType::LoadState::CANCELLED ? "CANCELLED" : \ + loadState == TextureManagerType::LoadState::LOAD_FAILED ? "LOAD_FAILED" : \ + "Unknown" +// clang-format on +#endif + +namespace +{ +const uint32_t DEFAULT_ATLAS_SIZE(1024u); ///< This size can fit 8 by 8 images of average size 128 * 128 +const Vector4 FULL_ATLAS_RECT(0.0f, 0.0f, 1.0f, 1.0f); ///< UV Rectangle that covers the full Texture + +void PreMultiply(Devel::PixelBuffer pixelBuffer, TextureManager::MultiplyOnLoad& preMultiplyOnLoad) +{ + if(Pixel::HasAlpha(pixelBuffer.GetPixelFormat())) + { + if(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD) + { + pixelBuffer.MultiplyColorByAlpha(); + } + } + else + { + preMultiplyOnLoad = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + } +} + +} // Anonymous namespace + +TextureManager::MaskingData::MaskingData() +: mAlphaMaskUrl(), + mAlphaMaskId(INVALID_TEXTURE_ID), + mContentScaleFactor(1.0f), + mCropToMask(true) +{ +} + +TextureManager::TextureManager() +: mTextureCacheManager(), + mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]() { return TextureAsyncLoadingHelper(*this); }), + mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]() { return TextureAsyncLoadingHelper(*this); }), + mLifecycleObservers(), + mLoadQueue(), + mQueueLoadFlag(false) +{ + // Initialize the AddOn + RenderingAddOn::Get(); +} + +TextureManager::~TextureManager() +{ + for(auto iter = mLifecycleObservers.Begin(), endIter = mLifecycleObservers.End(); iter != endIter; ++iter) + { + (*iter)->TextureManagerDestroyed(); + } +} + +TextureSet TextureManager::LoadAnimatedImageTexture( + Dali::AnimatedImageLoading animatedImageLoading, + const std::uint32_t& frameIndex, + const Dali::SamplingMode::Type& samplingMode, + const bool& synchronousLoading, + TextureManager::TextureId& textureId, + const Dali::WrapMode::Type& wrapModeU, + const Dali::WrapMode::Type& wrapModeV, + TextureUploadObserver* textureObserver) +{ + TextureSet textureSet; + + if(synchronousLoading) + { + Devel::PixelBuffer pixelBuffer; + if(animatedImageLoading) + { + pixelBuffer = animatedImageLoading.LoadFrame(frameIndex); + } + if(!pixelBuffer) + { + DALI_LOG_ERROR("TextureManager::LoadAnimatedImageTexture: Synchronous loading is failed\n"); + } + else + { + PixelData pixelData = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer + if(!textureSet) + { + Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight()); + texture.Upload(pixelData); + textureSet = TextureSet::New(); + textureSet.SetTexture(0u, texture); + } + } + } + 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, UseAtlas::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex, false); + TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId); + if(loadState == TextureManager::LoadState::UPLOADED) + { + // LoadComplete has already been called - keep the same texture set + textureSet = GetTextureSet(textureId); + } + } + + if(textureSet) + { + Sampler sampler = Sampler::New(); + sampler.SetWrapMode(wrapModeU, wrapModeV); + textureSet.SetSampler(0u, sampler); + } + + return textureSet; +} + +Devel::PixelBuffer TextureManager::LoadPixelBuffer( + const VisualUrl& url, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const bool& synchronousLoading, + TextureUploadObserver* textureObserver, + const bool& orientationCorrection, + TextureManager::MultiplyOnLoad& preMultiplyOnLoad) +{ + Devel::PixelBuffer pixelBuffer; + if(synchronousLoading) + { + if(url.IsValid()) + { + if(url.IsBufferResource()) + { + const EncodedImageBuffer& encodedImageBuffer = mTextureCacheManager.GetEncodedImageBuffer(url.GetUrl()); + if(encodedImageBuffer) + { + pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection); + } + } + else + { + pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection); + } + if(pixelBuffer && preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD) + { + PreMultiply(pixelBuffer, preMultiplyOnLoad); + } + } + } + else + { + RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false); + } + + return pixelBuffer; +} + +TextureSet TextureManager::LoadTexture( + const VisualUrl& url, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + MaskingDataPointer& maskInfo, + const bool& synchronousLoading, + TextureManager::TextureId& textureId, + Vector4& textureRect, + Dali::ImageDimensions& textureRectSize, + bool& atlasingStatus, + bool& loadingStatus, + const Dali::WrapMode::Type& wrapModeU, + const Dali::WrapMode::Type& wrapModeV, + TextureUploadObserver* textureObserver, + AtlasUploadObserver* atlasObserver, + ImageAtlasManagerPtr imageAtlasManager, + const bool& orientationCorrection, + const TextureManager::ReloadPolicy& reloadPolicy, + TextureManager::MultiplyOnLoad& preMultiplyOnLoad) +{ + TextureSet textureSet; + + loadingStatus = false; + textureRect = FULL_ATLAS_RECT; + + if(VisualUrl::TEXTURE == url.GetProtocolType()) + { + std::string location = url.GetLocation(); + if(location.size() > 0u) + { + TextureId id = std::stoi(location); + textureSet = mTextureCacheManager.GetExternalTextureSet(id); + if(textureSet) + { + preMultiplyOnLoad = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + textureId = id; + return textureSet; + } + } + } + else + { + // For Atlas + if(synchronousLoading && atlasingStatus && imageAtlasManager->CheckAtlasAvailable(url, desiredSize)) + { + Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection); + + if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid()) + { + 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(!textureSet) + { + atlasingStatus = false; + } + } + + if(!textureSet) + { + loadingStatus = true; + // Atlas manager can chage desired size when it is set by 0,0. + // We should store into textureRectSize only if atlasing successed. + // So copy inputed desiredSize, and replace value into textureRectSize only if atlasing success. + Dali::ImageDimensions atlasDesiredSize = desiredSize; + if(atlasingStatus) + { + textureSet = imageAtlasManager->Add(textureRect, url.GetUrl(), atlasDesiredSize, fittingMode, true, atlasObserver); + } + if(!textureSet) // big image, no atlasing or atlasing failed + { + atlasingStatus = false; + if(!maskInfo || !maskInfo->mAlphaMaskUrl.IsValid()) + { + textureId = RequestLoad( + url, + desiredSize, + fittingMode, + samplingMode, + UseAtlas::NO_ATLAS, + textureObserver, + orientationCorrection, + reloadPolicy, + preMultiplyOnLoad, + synchronousLoading); + } + else + { + maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, synchronousLoading); + textureId = RequestLoad( + url, + maskInfo->mAlphaMaskId, + maskInfo->mContentScaleFactor, + desiredSize, + fittingMode, + samplingMode, + UseAtlas::NO_ATLAS, + maskInfo->mCropToMask, + textureObserver, + orientationCorrection, + reloadPolicy, + preMultiplyOnLoad, + synchronousLoading); + } + + TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId); + if(loadState == TextureManager::LoadState::UPLOADED) + { + // LoadComplete has already been called - keep the same texture set + textureSet = mTextureCacheManager.GetTextureSet(textureId); + } + + // 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 = atlasDesiredSize; + } + } + } + + if(!atlasingStatus && textureSet) + { + Sampler sampler = Sampler::New(); + sampler.SetWrapMode(wrapModeU, wrapModeV); + textureSet.SetSampler(0u, sampler); + } + + if(synchronousLoading) + { + loadingStatus = false; + } + + return textureSet; +} + +TextureManager::TextureId TextureManager::RequestLoad( + const VisualUrl& url, + const ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const UseAtlas& useAtlas, + TextureUploadObserver* observer, + const bool& orientationCorrection, + const TextureManager::ReloadPolicy& reloadPolicy, + TextureManager::MultiplyOnLoad& preMultiplyOnLoad, + const 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, synchronousLoading); +} + +TextureManager::TextureId TextureManager::RequestLoad( + const VisualUrl& url, + const TextureManager::TextureId& maskTextureId, + const float& contentScale, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const TextureManager::UseAtlas& useAtlas, + const bool& cropToMask, + TextureUploadObserver* observer, + const bool& orientationCorrection, + const TextureManager::ReloadPolicy& reloadPolicy, + TextureManager::MultiplyOnLoad& preMultiplyOnLoad, + const bool& synchronousLoading) +{ + 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, + const 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, UseAtlas::NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading); +} + +TextureManager::TextureId TextureManager::RequestLoadInternal( + const VisualUrl& url, + const TextureManager::TextureId& maskTextureId, + const float& contentScale, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const TextureManager::UseAtlas& useAtlas, + const bool& cropToMask, + const TextureManager::StorageType& storageType, + TextureUploadObserver* observer, + const bool& orientationCorrection, + const TextureManager::ReloadPolicy& reloadPolicy, + TextureManager::MultiplyOnLoad& preMultiplyOnLoad, + Dali::AnimatedImageLoading animatedImageLoading, + const std::uint32_t& frameIndex, + const bool& synchronousLoading) +{ + // First check if the requested Texture is cached. + bool isAnimatedImage = (animatedImageLoading) ? true : false; + + TextureHash textureHash = INITIAL_HASH_NUMBER; + TextureCacheIndex cacheIndex = INVALID_CACHE_INDEX; + if(storageType != StorageType::RETURN_PIXEL_BUFFER && !isAnimatedImage) + { + textureHash = mTextureCacheManager.GenerateHash(url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId); + + // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision. + cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad); + } + + TextureManager::TextureId textureId = INVALID_TEXTURE_ID; + // Check if the requested Texture exists in the cache. + if(cacheIndex != INVALID_CACHE_INDEX) + { + if(TextureManager::ReloadPolicy::CACHED == reloadPolicy) + { + // Mark this texture being used by another client resource. Forced reload would replace the current texture + // without the need for incrementing the reference count. + ++(mTextureCacheManager[cacheIndex].referenceCount); + } + textureId = mTextureCacheManager[cacheIndex].textureId; + + // Update preMultiplyOnLoad value. It should be changed according to preMultiplied value of the cached info. + preMultiplyOnLoad = mTextureCacheManager[cacheIndex].preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + + 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); + } + + if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required + { + if(VisualUrl::BUFFER == url.GetProtocolType()) + { + std::string location = url.GetLocation(); + if(location.size() > 0u) + { + TextureId targetId = std::stoi(location); + const EncodedImageBuffer& encodedImageBuffer = mTextureCacheManager.GetEncodedImageBuffer(targetId); + if(encodedImageBuffer) + { + textureId = targetId; + + // Increase EncodedImageBuffer reference during it contains mTextureInfoContainer. + // TODO! We should change action when reload policy is FORCE. + // Eunki Hong will fix it after refactoring patch merged. + mTextureCacheManager.UseExternalResource(url.GetUrl()); + } + } + } + + if(textureId == INVALID_TEXTURE_ID) + { + textureId = mTextureCacheManager.GenerateUniqueTextureId(); + } + + bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD); + + // Cache new texutre, and get cacheIndex. + cacheIndex = mTextureCacheManager.AppendCache(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex)); + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId); + } + + // The below code path is common whether we are using the cache or not. + // The textureInfoIndex now refers to either a pre-existing cached TextureInfo, + // or a new TextureInfo just created. + TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]); + textureInfo.maskTextureId = maskTextureId; + textureInfo.storageType = storageType; + textureInfo.orientationCorrection = orientationCorrection; + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureInfo loadState:%s\n", GET_LOAD_STATE_STRING(textureInfo.loadState)); + + // Force reloading of texture by setting loadState unless already loading or cancelled. + if(TextureManager::ReloadPolicy::FORCED == reloadPolicy && + TextureManager::LoadState::LOADING != textureInfo.loadState && + TextureManager::LoadState::WAITING_FOR_MASK != textureInfo.loadState && + TextureManager::LoadState::MASK_APPLYING != textureInfo.loadState && + TextureManager::LoadState::MASK_APPLIED != textureInfo.loadState && + TextureManager::LoadState::CANCELLED != textureInfo.loadState) + { + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId); + + textureInfo.loadState = TextureManager::LoadState::NOT_STARTED; + } + + if(!synchronousLoading) + { + // 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) + { + 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: + { + 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; + } + } + } + else + { + // If the image is already finished to load, use cached texture. + // We don't need to consider Observer because this is synchronous loading. + if(textureInfo.loadState == TextureManager::LoadState::UPLOADED || + textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED) + { + return textureId; + } + else + { + Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection); + + if(!pixelBuffer) + { + // 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) + { + TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId); + if(maskCacheIndex != INVALID_CACHE_INDEX) + { + Devel::PixelBuffer maskPixelBuffer = mTextureCacheManager[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); + } + } + } + + // Return the TextureId for which this Texture can now be referenced by externally. + return textureId; +} + +void TextureManager::Remove(const TextureManager::TextureId& textureId, TextureUploadObserver* observer) +{ + // Remove textureId in CacheManager. + mTextureCacheManager.RemoveCache(textureId); + + if(observer) + { + // Remove element from the LoadQueue + for(auto&& element : mLoadQueue) + { + if(element.mObserver == observer) + { + // Do not erase the item. We will clear it later in ProcessQueuedTextures(). + element.mObserver = nullptr; + break; + } + } + } +} + +Devel::PixelBuffer TextureManager::LoadImageSynchronously( + const VisualUrl& url, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const bool& orientationCorrection) +{ + Devel::PixelBuffer pixelBuffer; + if(url.IsBufferResource()) + { + const EncodedImageBuffer& encodedImageBuffer = mTextureCacheManager.GetEncodedImageBuffer(url.GetUrl()); + if(encodedImageBuffer) + { + pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection); + } + } + else + { + pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection); + } + return pixelBuffer; +} + +void TextureManager::AddObserver(TextureManager::LifecycleObserver& observer) +{ + // make sure an observer doesn't observe the same object twice + // otherwise it will get multiple calls to ObjectDestroyed() + DALI_ASSERT_DEBUG(mLifecycleObservers.End() == std::find(mLifecycleObservers.Begin(), mLifecycleObservers.End(), &observer)); + mLifecycleObservers.PushBack(&observer); +} + +void TextureManager::RemoveObserver(TextureManager::LifecycleObserver& observer) +{ + // Find the observer... + auto endIter = mLifecycleObservers.End(); + for(auto iter = mLifecycleObservers.Begin(); iter != endIter; ++iter) + { + if((*iter) == &observer) + { + mLifecycleObservers.Erase(iter); + break; + } + } + DALI_ASSERT_DEBUG(endIter != mLifecycleObservers.End()); +} + +void TextureManager::LoadOrQueueTexture(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer) +{ + switch(textureInfo.loadState) + { + case LoadState::NOT_STARTED: + case LoadState::LOAD_FAILED: + { + if(mQueueLoadFlag) + { + QueueLoadTexture(textureInfo, observer); + } + else + { + LoadTexture(textureInfo, observer); + } + break; + } + case LoadState::UPLOADED: + { + if(mQueueLoadFlag) + { + QueueLoadTexture(textureInfo, observer); + } + else + { + // The Texture has already loaded. The other observers have already been notified. + // We need to send a "late" loaded notification for this observer. + observer->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, textureInfo.textureId, textureInfo.textureSet, (textureInfo.useAtlas == UseAtlas::USE_ATLAS) ? true : false, textureInfo.atlasRect, textureInfo.preMultiplied)); + } + break; + } + case LoadState::LOADING: + case LoadState::CANCELLED: + case LoadState::LOAD_FINISHED: + case LoadState::WAITING_FOR_MASK: + case LoadState::MASK_APPLYING: + case LoadState::MASK_APPLIED: + { + break; + } + } +} + +void TextureManager::QueueLoadTexture(const TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer) +{ + const auto& textureId = textureInfo.textureId; + mLoadQueue.PushBack(LoadQueueElement(textureId, observer)); + + observer->DestructionSignal().Connect(this, &TextureManager::ObserverDestroyed); +} + +void TextureManager::LoadTexture(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer) +{ + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::LoadTexture(): url:%s sync:%s\n", textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F"); + + textureInfo.loadState = LoadState::LOADING; + if(!textureInfo.loadSynchronously) + { + auto& loadersContainer = (textureInfo.url.IsLocalResource() || textureInfo.url.IsBufferResource()) ? mAsyncLocalLoaders : mAsyncRemoteLoaders; + auto loadingHelperIt = loadersContainer.GetNext(); + auto premultiplyOnLoad = (textureInfo.preMultiplyOnLoad && textureInfo.maskTextureId == INVALID_TEXTURE_ID) ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF; + DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End()); + if(textureInfo.animatedImageLoading) + { + loadingHelperIt->LoadAnimatedImage(textureInfo.textureId, textureInfo.animatedImageLoading, textureInfo.frameIndex); + } + else + { + loadingHelperIt->Load(textureInfo.textureId, textureInfo.url, textureInfo.desiredSize, textureInfo.fittingMode, textureInfo.samplingMode, textureInfo.orientationCorrection, premultiplyOnLoad); + } + } + ObserveTexture(textureInfo, observer); +} + +void TextureManager::ProcessQueuedTextures() +{ + for(auto&& element : mLoadQueue) + { + if(!element.mObserver) + { + continue; + } + + TextureCacheIndex cacheIndex = mTextureCacheManager.GetCacheIndexFromId(element.mTextureId); + if(cacheIndex != INVALID_CACHE_INDEX) + { + TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]); + if(textureInfo.loadState == LoadState::UPLOADED) + { + element.mObserver->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, textureInfo.textureId, textureInfo.textureSet, (textureInfo.useAtlas == UseAtlas::USE_ATLAS) ? true : false, textureInfo.atlasRect, textureInfo.preMultiplied)); + } + else if(textureInfo.loadState == LoadState::LOAD_FINISHED && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER) + { + element.mObserver->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::PIXEL_BUFFER, textureInfo.pixelBuffer, textureInfo.url.GetUrl(), textureInfo.preMultiplied)); + } + else + { + LoadTexture(textureInfo, element.mObserver); + } + } + } + mLoadQueue.Clear(); +} + +void TextureManager::ObserveTexture(TextureManager::TextureInfo& textureInfo, + TextureUploadObserver* observer) +{ + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::ObserveTexture(): url:%s observer:%p\n", textureInfo.url.GetUrl().c_str(), observer); + + if(observer) + { + textureInfo.observerList.PushBack(observer); + observer->DestructionSignal().Connect(this, &TextureManager::ObserverDestroyed); + } +} + +void TextureManager::AsyncLoadComplete(const TextureManager::TextureId& textureId, Devel::PixelBuffer pixelBuffer) +{ + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( textureId:%d )\n", textureId); + TextureCacheIndex cacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureId); + if(cacheIndex != INVALID_CACHE_INDEX) + { + TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]); + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, " textureId:%d Url:%s CacheIndex:%d LoadState: %s\n", textureInfo.textureId, textureInfo.url.GetUrl().c_str(), cacheIndex, GET_LOAD_STATE_STRING(textureInfo.loadState)); + + if(textureInfo.loadState != LoadState::CANCELLED) + { + // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified) + PostLoad(textureInfo, pixelBuffer); + } + else + { + Remove(textureInfo.textureId, nullptr); + } + } +} + +void TextureManager::PostLoad(TextureManager::TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer) +{ + // Was the load successful? + if(pixelBuffer && (pixelBuffer.GetWidth() != 0) && (pixelBuffer.GetHeight() != 0)) + { + // No atlas support for now + textureInfo.useAtlas = UseAtlas::NO_ATLAS; + textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied(); + + if(textureInfo.storageType == StorageType::UPLOAD_TO_TEXTURE) + { + // 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. + // 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) + { + textureInfo.loadState = LoadState::MASK_APPLIED; + UploadTexture(pixelBuffer, textureInfo); + NotifyObservers(textureInfo, true); + } + else + { + LoadState maskLoadState = mTextureCacheManager.GetTextureStateInternal(textureInfo.maskTextureId); + textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily + if(maskLoadState == LoadState::LOADING) + { + textureInfo.loadState = LoadState::WAITING_FOR_MASK; + } + else if(maskLoadState == LoadState::LOAD_FINISHED) + { + // Send New Task to Thread + ApplyMask(textureInfo, textureInfo.maskTextureId); + } + } + } + else + { + UploadTexture(pixelBuffer, textureInfo); + NotifyObservers(textureInfo, true); + } + } + else + { + textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data + textureInfo.loadState = LoadState::LOAD_FINISHED; + + if(textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER) + { + NotifyObservers(textureInfo, true); + } + else + { + // Check if there was another texture waiting for this load to complete + // (e.g. if this was an image mask, and its load is on a different thread) + CheckForWaitingTexture(textureInfo); + } + } + } + else + { + textureInfo.loadState = LoadState::LOAD_FAILED; + CheckForWaitingTexture(textureInfo); + NotifyObservers(textureInfo, false); + } +} + +void TextureManager::CheckForWaitingTexture(TextureManager::TextureInfo& maskTextureInfo) +{ + // Search the cache, checking if any texture has this texture id as a + // maskTextureId: + const TextureCacheIndex size = static_cast(mTextureCacheManager.size()); + + for(TextureCacheIndex cacheIndex = 0; cacheIndex < size; ++cacheIndex) + { + if(mTextureCacheManager[cacheIndex].maskTextureId == maskTextureInfo.textureId && + mTextureCacheManager[cacheIndex].loadState == LoadState::WAITING_FOR_MASK) + { + TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]); + + if(maskTextureInfo.loadState == LoadState::LOAD_FINISHED) + { + // Send New Task to Thread + ApplyMask(textureInfo, maskTextureInfo.textureId); + } + else + { + textureInfo.pixelBuffer.Reset(); + textureInfo.loadState = LoadState::LOAD_FAILED; + NotifyObservers(textureInfo, false); + } + } + } +} + +void TextureManager::ApplyMask(TextureManager::TextureInfo& textureInfo, const TextureManager::TextureId& maskTextureId) +{ + TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId); + if(maskCacheIndex != INVALID_CACHE_INDEX) + { + Devel::PixelBuffer maskPixelBuffer = mTextureCacheManager[maskCacheIndex].pixelBuffer; + Devel::PixelBuffer pixelBuffer = textureInfo.pixelBuffer; + textureInfo.pixelBuffer.Reset(); + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::ApplyMask(): url:%s sync:%s\n", textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F"); + + textureInfo.loadState = LoadState::MASK_APPLYING; + auto& loadersContainer = (textureInfo.url.IsLocalResource() || textureInfo.url.IsBufferResource()) ? mAsyncLocalLoaders : mAsyncRemoteLoaders; + auto loadingHelperIt = loadersContainer.GetNext(); + auto premultiplyOnLoad = textureInfo.preMultiplyOnLoad ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF; + DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End()); + loadingHelperIt->ApplyMask(textureInfo.textureId, pixelBuffer, maskPixelBuffer, textureInfo.scaleFactor, textureInfo.cropToMask, premultiplyOnLoad); + } +} + +void TextureManager::UploadTexture(Devel::PixelBuffer& pixelBuffer, TextureManager::TextureInfo& textureInfo) +{ + if(textureInfo.loadState != LoadState::UPLOADED && textureInfo.useAtlas != UseAtlas::USE_ATLAS) + { + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, " TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId); + + // Check if this pixelBuffer is premultiplied + textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied(); + + auto& renderingAddOn = RenderingAddOn::Get(); + if(renderingAddOn.IsValid()) + { + renderingAddOn.CreateGeometry(textureInfo.textureId, pixelBuffer); + } + + Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight()); + + PixelData pixelData = Devel::PixelBuffer::Convert(pixelBuffer); + texture.Upload(pixelData); + if(!textureInfo.textureSet) + { + textureInfo.textureSet = TextureSet::New(); + } + textureInfo.textureSet.SetTexture(0u, texture); + } + + // Update the load state. + // Note: This is regardless of success as we care about whether a + // load attempt is in progress or not. If unsuccessful, a broken + // image is still loaded. + textureInfo.loadState = LoadState::UPLOADED; +} + +void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, const bool& success) +{ + TextureId textureId = textureInfo.textureId; + + // If there is an observer: Notify the load is complete, whether successful or not, + // and erase it from the list + TextureInfo* info = &textureInfo; + + mQueueLoadFlag = true; + + while(info->observerList.Count()) + { + TextureUploadObserver* observer = info->observerList[0]; + + // During LoadComplete() a Control ResourceReady() signal is emitted. + // During that signal the app may add remove /add Textures (e.g. via + // ImageViews). + // It is possible for observers to be removed from the observer list, + // and it is also possible for the mTextureInfoContainer to be modified, + // invalidating the reference to the textureInfo struct. + // Texture load requests for the same URL are deferred until the end of this + // method. + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::NotifyObservers() textureId:%d url:%s loadState:%s\n", textureId, textureInfo.url.GetUrl().c_str(), GET_LOAD_STATE_STRING(textureInfo.loadState)); + + // It is possible for the observer to be deleted. + // Disconnect and remove the observer first. + observer->DestructionSignal().Disconnect(this, &TextureManager::ObserverDestroyed); + + info->observerList.Erase(info->observerList.Begin()); + + if(info->storageType == StorageType::RETURN_PIXEL_BUFFER) + { + observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::PIXEL_BUFFER, info->pixelBuffer, info->url.GetUrl(), info->preMultiplied)); + } + else + { + observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, info->textureId, info->textureSet, (info->useAtlas == UseAtlas::USE_ATLAS) ? true : false, info->atlasRect, info->preMultiplied)); + } + + // Get the textureInfo from the container again as it may have been invalidated. + TextureCacheIndex textureInfoIndex = mTextureCacheManager.GetCacheIndexFromId(textureId); + if(textureInfoIndex == INVALID_CACHE_INDEX) + { + break; // texture has been removed - can stop. + } + info = &mTextureCacheManager[textureInfoIndex]; + } + + mQueueLoadFlag = false; + ProcessQueuedTextures(); + + if(info->storageType == StorageType::RETURN_PIXEL_BUFFER && info->observerList.Count() == 0) + { + Remove(info->textureId, nullptr); + } +} + +void TextureManager::ObserverDestroyed(TextureUploadObserver* observer) +{ + const TextureCacheIndex count = static_cast(mTextureCacheManager.size()); + for(TextureCacheIndex i = 0; i < count; ++i) + { + TextureInfo& textureInfo(mTextureCacheManager[i]); + for(TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin(); + j != textureInfo.observerList.End();) + { + if(*j == observer) + { + j = textureInfo.observerList.Erase(j); + } + else + { + ++j; + } + } + } + + // Remove element from the LoadQueue + for(auto&& element : mLoadQueue) + { + if(element.mObserver == observer) + { + element.mObserver = nullptr; + } + } +} + +Dali::Geometry TextureManager::GetRenderGeometry(const TextureManager::TextureId& textureId, std::uint32_t& frontElements, std::uint32_t& backElements) +{ + return RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().GetGeometry(textureId, frontElements, backElements) : Geometry(); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/texture-manager/texture-manager-impl.h b/dali-toolkit/internal/texture-manager/texture-manager-impl.h new file mode 100644 index 0000000..ee06c53 --- /dev/null +++ b/dali-toolkit/internal/texture-manager/texture-manager-impl.h @@ -0,0 +1,627 @@ +#ifndef DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H +#define DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H + +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +class ImageAtlasManager; +typedef IntrusivePtr ImageAtlasManagerPtr; +class TextureAsyncLoadingHelper; + +/** + * The TextureManager provides a common Image loading API for Visuals. + * + * The TextureManager is responsible for providing sync, async, atlased and non-atlased + * CPU time alpha masking, animated image loads. + * + * Texture caching is provided and performed by TextureCacheManager. + * TextureUploadObserver.LoadComplete called when async load completed. + */ +class TextureManager : public ConnectionTracker +{ +public: + // Copy enum and types and const values that TextureManager will use. + using TextureId = TextureManagerType::TextureId; + using TextureCacheIndex = TextureManagerType::TextureCacheIndex; + using TextureHash = TextureManagerType::TextureHash; + + static constexpr TextureId INVALID_TEXTURE_ID = TextureManagerType::INVALID_TEXTURE_ID; + static constexpr TextureCacheIndex INVALID_CACHE_INDEX = TextureManagerType::INVALID_CACHE_INDEX; + + using UseAtlas = TextureManagerType::UseAtlas; + using StorageType = TextureManagerType::StorageType; + using LoadType = TextureManagerType::LoadType; + using LoadState = TextureManagerType::LoadState; + using ReloadPolicy = TextureManagerType::ReloadPolicy; + using MultiplyOnLoad = TextureManagerType::MultiplyOnLoad; + using TextureInfo = TextureManagerType::TextureInfo; + +public: + struct MaskingData + { + MaskingData(); + ~MaskingData() = default; + + VisualUrl mAlphaMaskUrl; + TextureManager::TextureId mAlphaMaskId; + float mContentScaleFactor; + bool mCropToMask; + }; + using MaskingDataPointer = std::unique_ptr; + + /** + * Class to provide lifecycle event on destruction of texture manager. + */ + struct LifecycleObserver + { + /** + * Called shortly before the texture manager is destroyed. + */ + virtual void TextureManagerDestroyed() = 0; + }; + + /** + * Constructor. + */ + TextureManager(); + + /** + * Destructor. + */ + ~TextureManager() override; + + // TextureManager Main API: + + /** + * @brief Requests an frame of animated image load. + * + * The parameters are used to specify how the animated image is loaded. + * The observer has the LoadComplete method called when the load is ready. + * + * @param[in] animatedImageLoading The AnimatedImageLoading that contain the animated image information + * @param[in] frameIndex The frame index to load. + * @param[in] samplingMode The SamplingMode to use + * @param[in] synchronousLoading true if the frame should be loaded synchronously + * @param[out] textureId The textureId of the frame + * @param[in] wrapModeU Horizontal Wrap mode + * @param[in] wrapModeV Vertical Wrap mode + * @param[in] textureObserver The client object should inherit from this and provide the "UploadCompleted" virtual. + * This is called when an image load completes (or fails). + * + * @return The texture set containing the frame of animated image, or empty if still loading. + */ + TextureSet LoadAnimatedImageTexture( + Dali::AnimatedImageLoading animatedImageLoading, + const std::uint32_t& frameIndex, + const Dali::SamplingMode::Type& samplingMode, + const bool& synchronousLoading, + TextureManager::TextureId& textureId, + const Dali::WrapMode::Type& wrapModeU, + const Dali::WrapMode::Type& wrapModeV, + TextureUploadObserver* textureObserver); + + /** + * @brief Requests an image load of the given URL to get PixelBuffer. + * + * The parameters are used to specify how the image is loaded. + * The observer has the LoadComplete method called when the load is ready. + * + * @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] synchronousLoading true if the URL should be loaded synchronously + * @param[in] textureObserver The client object should inherit from this and provide the "UploadCompleted" virtual. + * This is called when an image load completes (or fails). + * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data + * @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 + * + * @return The pixel buffer containing the image, or empty if still loading. + */ + Devel::PixelBuffer LoadPixelBuffer( + const VisualUrl& url, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const bool& synchronousLoading, + TextureUploadObserver* textureObserver, + const bool& orientationCorrection, + TextureManager::MultiplyOnLoad& preMultiplyOnLoad); + + /** + * @brief Requests an image load of the given URL. + * + * The parameters are used to specify how the image is loaded. + * The observer has the LoadComplete method called when the load is ready. + * + * When the client has finished with the Texture, Remove() should be called. + * + * @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, out] maskInfo Mask info structure + * @param[in] synchronousLoading true if the URL should be loaded synchronously + * @param[out] textureId, The textureId of the URL + * @param[out] textureRect The rectangle within the texture atlas that this URL occupies, + * this is the rectangle in normalized coordinates. + * @param[out] textureRectSize The rectangle within the texture atlas that this URL occupies, + * this is the same rectangle in pixels. + * @param[in,out] atlasingStatus Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still + * be loaded, and marked successful, but this will be set to false. + * If atlasing succeeds, this will be set to true. + * @param[out] loadingStatus The loading status of the texture + * @param[in] wrapModeU Horizontal Wrap mode + * @param[in] wrapModeV Vertical Wrap mode + * @param[in] textureObserver The client object should inherit from this and provide the "UploadCompleted" virtual. + * This is called when an image load completes (or fails). + * @param[in] atlasObserver This is used if the texture is atlased, and will be called instead of + * textureObserver.UploadCompleted + * @param[in] imageAtlasManager The atlas manager to use for atlasing textures + * @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 + * + * @return The texture set containing the image, or empty if still loading. + */ + TextureSet LoadTexture( + const VisualUrl& url, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + MaskingDataPointer& maskInfo, + const bool& synchronousLoading, + TextureManager::TextureId& textureId, + Dali::Vector4& textureRect, + Dali::ImageDimensions& textureRectSize, + bool& atlasingStatus, + bool& loadingStatus, + const Dali::WrapMode::Type& wrapModeU, + const Dali::WrapMode::Type& wrapModeV, + TextureUploadObserver* textureObserver, + AtlasUploadObserver* atlasObserver, + ImageAtlasManagerPtr imageAtlasManager, + const bool& orientationCorrection, + const TextureManager::ReloadPolicy& reloadPolicy, + TextureManager::MultiplyOnLoad& preMultiplyOnLoad); + + /** + * @brief Remove a Texture from the TextureManager. + * + * Textures are cached and therefore only the removal of the last + * occurrence of a Texture will cause its removal internally. + * + * @param[in] textureId The ID of the Texture to remove. + * @param[in] textureObserver The texture observer. + */ + void Remove(const TextureManager::TextureId& textureId, TextureUploadObserver* textureObserver); + + /** + * Add an observer to the object. + * @param[in] observer The observer to add. + */ + void AddObserver(TextureManager::LifecycleObserver& observer); + + /** + * Remove an observer from the object + * @pre The observer has already been added. + * @param[in] observer The observer to remove. + */ + void RemoveObserver(TextureManager::LifecycleObserver& observer); + + /** + * @brief Returns the geometry associated with texture. + * @param[in] textureId Id of the texture + * @param[out] frontElements number of front elements + * @param[out] backElements number of back elements + * @return Returns valid geometry object + */ + Geometry GetRenderGeometry(const TextureManager::TextureId& textureId, std::uint32_t& frontElements, std::uint32_t& backElements); + +public: + // API list that need to access TextureCacheManager. + + /** + * @copydoc TextureCacheManager::GetVisualUrl + */ + inline VisualUrl GetVisualUrl(const TextureManager::TextureId& textureId) + { + return mTextureCacheManager.GetVisualUrl(textureId); + } + + /** + * @copydoc TextureCacheManager::GetTextureSet + */ + inline TextureSet GetTextureSet(const TextureManager::TextureId& textureId) + { + return mTextureCacheManager.GetTextureSet(textureId); + } + + /** + * @copydoc TextureCacheManager::RemoveExternalTexture + */ + inline TextureSet RemoveExternalTexture(const std::string& url) + { + return mTextureCacheManager.RemoveExternalTexture(url); + } + + /** + * @copydoc TextureCacheManager::RemoveExternalEncodedImageBuffer + */ + inline EncodedImageBuffer RemoveExternalEncodedImageBuffer(const std::string& url) + { + return mTextureCacheManager.RemoveExternalEncodedImageBuffer(url); + } + + /** + * @copydoc TextureCacheManager::UseExternalResource + */ + inline void UseExternalResource(const VisualUrl& url) + { + mTextureCacheManager.UseExternalResource(url); + } + + /** + * @copydoc TextureCacheManager::GetEncodedImageBuffer + */ + inline EncodedImageBuffer GetEncodedImageBuffer(const std::string& url) + { + return mTextureCacheManager.GetEncodedImageBuffer(url); + } + + /** + * @copydoc TextureCacheManager::AddExternalTexture + */ + inline std::string AddExternalTexture(const TextureSet& texture) + { + return mTextureCacheManager.AddExternalTexture(texture); + } + + /** + * @copydoc TextureCacheManager::AddExternalEncodedImageBuffer + */ + inline std::string AddExternalEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer) + { + return mTextureCacheManager.AddExternalEncodedImageBuffer(encodedImageBuffer); + } + +public: // Load Request API + /** + * @brief Requests an image load of the given URL. + * + * The parameters are used to specify how the image is loaded. + * The observer has the LoadComplete method called when the load is ready. + * + * When the client has finished with the Texture, Remove() should be called. + * + * @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] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful, + * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver. + * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual. + * This is called when an image load completes (or fails). + * @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. If you skip this parameter, + * default is false. + * @return A TextureId to use as a handle to reference this Texture + */ + TextureManager::TextureId RequestLoad( + const VisualUrl& url, + const ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const TextureManager::UseAtlas& useAtlasing, + TextureUploadObserver* observer, + const bool& orientationCorrection, + const TextureManager::ReloadPolicy& reloadPolicy, + TextureManager::MultiplyOnLoad& preMultiplyOnLoad, + const bool& synchronousLoading = false); + + /** + * @brief Requests an image load of the given URL, when the texture has + * have loaded, it will perform a blend with the image mask, and upload + * the blended texture. + * + * The parameters are used to specify how the image is loaded. + * The observer has the LoadComplete method called when the load is ready. + * + * When the client has finished with the Texture, Remove() should be called. + * + * @param[in] url The URL of the image to load + * @param[in] maskTextureId The texture id of an image to mask this with + * (can be INVALID if no masking required) + * @param[in] contentScale The scale factor to apply to the image before masking + * @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] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still + * be loaded, and marked successful, + * but "useAtlasing" will be set to false in the "UploadCompleted" callback from + * the TextureManagerUploadObserver. + * @param[in] cropToMask Only used with masking, this will crop the scaled image to the mask size. + * If false, then the mask will be scaled to fit the image before being applied. + * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" + * virtual. + * This is called when an image load completes (or fails). + * @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. If you skip this parameter, + * default is false. + * @return A TextureId to use as a handle to reference this Texture + */ + TextureManager::TextureId RequestLoad( + const VisualUrl& url, + const TextureManager::TextureId& maskTextureId, + const float& contentScale, + const ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const TextureManager::UseAtlas& useAtlasing, + const bool& cropToMask, + TextureUploadObserver* observer, + const bool& orientationCorrection, + const TextureManager::ReloadPolicy& reloadPolicy, + TextureManager::MultiplyOnLoad& preMultiplyOnLoad, + const bool& synchronousLoading = false); + + /** + * @brief 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. + * @param[in] maskUrl The URL of the mask image to load + * @param[in] synchronousLoading True if the frame should be loaded synchronously. If you skip this parameter, + * default is false. + * @return A TextureId to use as a handle to reference this mask Texture + */ + TextureManager::TextureId RequestMaskLoad( + const VisualUrl& maskUrl, + const bool& synchronousLoading = false); + +private: + /** + * @brief Requests an image load of the given URL, when the texture has + * have loaded, if there is a valid maskTextureId, it will perform a + * CPU blend with the mask, and upload the blend texture. + * + * The parameters are used to specify how the image is loaded. + * The observer has the LoadComplete method called when the load is ready. + * + * When the client has finished with the Texture, Remove() should be called. + * + * @param[in] url The URL of the image to load + * @param[in] maskTextureId The texture id of an image to use as a mask. If no mask is required, then set + * to INVALID_TEXTURE_ID + * @param[in] contentScale The scaling factor to apply to the content when masking + * @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] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be + * loaded, and marked successful, but "useAtlasing" will be set to false in the + * "UploadCompleted" callback from the TextureManagerUploadObserver. + * @param[in] cropToMask Whether to crop the target after masking, or scale the mask to the image before + * masking. + * @param[in] storageType, Whether the pixel data is stored in the cache or uploaded to the GPU + * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" + * virtual. + * This is called when an image load completes (or fails). + * @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 + * 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. If you skip this parameter, + * default is false. + * @return A TextureId to use as a handle to reference this Texture + */ + TextureManager::TextureId RequestLoadInternal( + const VisualUrl& url, + const TextureManager::TextureId& maskTextureId, + const float& contentScale, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const TextureManager::UseAtlas& useAtlas, + const bool& cropToMask, + const TextureManager::StorageType& storageType, + TextureUploadObserver* observer, + const bool& orientationCorrection, + const TextureManager::ReloadPolicy& reloadPolicy, + TextureManager::MultiplyOnLoad& preMultiplyOnLoad, + Dali::AnimatedImageLoading animatedImageLoading, + const std::uint32_t& frameIndex, + const bool& synchronousLoading); + + /** + * @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 Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const bool& orientationCorrection); + +private: + // Load and queue + + /** + * Structure to hold info about a texture load queued during NotifyObservers + */ + struct LoadQueueElement + { + LoadQueueElement(TextureManager::TextureId textureId, TextureUploadObserver* observer) + : mTextureId(textureId), + mObserver(observer) + { + } + + TextureManager::TextureId mTextureId; ///< The texture id of the requested load. + TextureUploadObserver* mObserver; ///< Observer of texture load. + }; + + /** + * @brief Initiate a load or queue load if NotifyObservers is invoking callbacks + * @param[in] textureInfo The TextureInfo struct associated with the Texture + * @param[in] observer The observer wishing to observe the texture upload + */ + void LoadOrQueueTexture(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer); + + /** + * @brief Queue a texture load to be subsequently handled by ProcessQueuedTextures. + * @param[in] textureInfo The TextureInfo struct associated with the Texture + * @param[in] observer The observer wishing to observe the texture upload + */ + void QueueLoadTexture(const TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer); + + /** + * @brief Used internally to initiate a load. + * @param[in] textureInfo The TextureInfo struct associated with the Texture + * @param[in] observer The observer wishing to observe the texture upload + */ + void LoadTexture(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer); + + /** + * @brief Initiate load of textures queued whilst NotifyObservers invoking callbacks. + */ + void ProcessQueuedTextures(); + + /** + * Add the observer to the observer list + * @param[in] textureInfo The TextureInfo struct associated with the texture + * @param[in] observer The observer wishing to observe the texture upload + */ + void ObserveTexture(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer); + + /** + * @brief Performs Post-Load steps including atlasing. + * @param[in] textureInfo The struct associated with this Texture + * @param[in] pixelBuffer The image pixelBuffer + * @return True if successful + */ + void PostLoad(TextureManager::TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer); + + /** + * Check if there is a texture waiting to be masked. If there + * is then apply this mask and upload it. + * @param[in] maskTextureInfo The texture info of the mask that has just loaded. + */ + void CheckForWaitingTexture(TextureManager::TextureInfo& maskTextureInfo); + + /** + * Apply the mask to the pixelBuffer. + * @param[in] textureInfo The information of texture to apply the mask to + * @param[in] maskTextureId The texture id of the mask. + */ + void ApplyMask(TextureManager::TextureInfo& textureInfo, const TextureManager::TextureId& maskTextureId); + + /** + * Upload the texture specified in pixelBuffer to the appropriate location + * @param[in] pixelBuffer The image data to upload + * @param[in] textureInfo The texture info containing the location to + * store the data to. + */ + void UploadTexture(Devel::PixelBuffer& pixelBuffer, TextureManager::TextureInfo& textureInfo); + + /** + * Notify the current observers that the texture upload is complete, + * then remove the observers from the list. + * @param[in] textureInfo The struct associated with this Texture + * @param[in] success If the pixel data was retrieved successfully and uploaded to GPU + */ + void NotifyObservers(TextureManager::TextureInfo& textureInfo, const bool& success); + +public: + /** + * @brief Common method to handle loading completion. + * TextureAsyncLoadingHelper will call this API After async loading finished. + * @param[in] textureId The ID of the texture load complete. + * @param[in] pixelBuffer The loaded image data + */ + void AsyncLoadComplete(const TextureManager::TextureId& textureId, Devel::PixelBuffer pixelBuffer); + +private: + /** + * Deleted copy constructor. + */ + TextureManager(const TextureManager&) = delete; + + /** + * Deleted assignment operator. + */ + TextureManager& operator=(const TextureManager& rhs) = delete; + + /** + * This is called by the TextureManagerUploadObserver when an observer is destroyed. + * We use the callback to know when to remove an observer from our notify list. + * @param[in] observer The observer that generated the callback + */ + void ObserverDestroyed(TextureUploadObserver* observer); + +private: // Member Variables: + TextureCacheManager mTextureCacheManager; ///< Manager the life-cycle and caching of Textures + + RoundRobinContainerView mAsyncLocalLoaders; ///< The Asynchronous image loaders used to provide all local async loads + RoundRobinContainerView mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads + + Dali::Vector mLifecycleObservers; ///< Lifecycle observers of texture manager + Dali::Vector mLoadQueue; ///< Queue of textures to load after NotifyObservers + bool mQueueLoadFlag; ///< Flag that causes Load Textures to be queued. +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H diff --git a/dali-toolkit/internal/texture-manager/texture-manager-type.h b/dali-toolkit/internal/texture-manager/texture-manager-type.h new file mode 100644 index 0000000..5f07c1f --- /dev/null +++ b/dali-toolkit/internal/texture-manager/texture-manager-type.h @@ -0,0 +1,206 @@ +#ifndef DALI_TOOLKIT_TEXTURE_MANAGER_TYPE_H +#define DALI_TOOLKIT_TEXTURE_MANAGER_TYPE_H + +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// EXTERNAL INCLUDES +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +/** + * @brief Define common type, enum, and struct that both TextureManager and TextureCacheManager will use. + */ +namespace TextureManagerType +{ +// Typedef: + +typedef std::int32_t TextureId; ///< The TextureId type. This is used as a handle to refer to a particular Texture. +typedef std::int32_t TextureCacheIndex; ///< The TextureCacheIndex type. This is used as a handles to refer to a particular Texture in TextureCacheManager. + /// Note : For the same Texture, TextureId will not be changed. But TextureCacheIndex can be chaged when TextureCacheManager + /// Internal container informations changed by Append or Remove. +typedef std::size_t TextureHash; ///< The type used to store the hash used for Texture caching. + +// Constant values: + +static constexpr TextureId INVALID_TEXTURE_ID = -1; ///< Used to represent a null TextureId or error +static constexpr TextureCacheIndex INVALID_CACHE_INDEX = -1; ///< Used to represent a null TextureCacheIndex or error + +// Enum classes: + +/** + * Whether the texture should be atlased or uploaded into it's own GPU texture + */ +enum class UseAtlas +{ + NO_ATLAS, + USE_ATLAS +}; + +/** + * Whether the pixel data should be kept in TextureManager, returned with pixelBuffer or uploaded for rendering + */ +enum class StorageType +{ + 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. +}; + +/** + * Whether the texture should be loaded synchronously or asynchronously. + */ +enum class LoadType +{ + LOAD_ASYNCHRONOUSLY, + LOAD_SYNCHRONOUSLY +}; + +/** + * @brief The LoadState Enumeration represents the current state of a particular Texture's life-cycle. + */ +enum class LoadState +{ + NOT_STARTED, ///< Default + LOADING, ///< Loading has been started, but not finished. + LOAD_FINISHED, ///< Loading has finished. (for CPU storage only) + WAITING_FOR_MASK, ///< Loading has finished, but waiting for mask image + MASK_APPLYING, ///< Loading has finished, Mask is applying + MASK_APPLIED, ///< Loading has finished, Mask is applyied by GPU + UPLOADED, ///< Uploaded and ready. (For GPU upload only) + CANCELLED, ///< Removed before loading completed + LOAD_FAILED ///< Async loading failed, e.g. connection problem +}; + +/** + * @brief Types of reloading policies + */ +enum class ReloadPolicy +{ + CACHED = 0, ///< Loads cached texture if it exists. + FORCED ///< Forces reloading of texture. +}; + +/** + * @brief Whether to multiply alpha into color channels on load + */ +enum class MultiplyOnLoad +{ + LOAD_WITHOUT_MULTIPLY = 0, ///< Don't modify the image + MULTIPLY_ON_LOAD ///< Multiply alpha into color channels on load +}; + +// Structs: + +/** + * @brief This struct is used to manage the life-cycle of Texture loading and caching. + */ +struct TextureInfo +{ + TextureInfo(const TextureId& textureId, + const TextureId& maskTextureId, + const VisualUrl& url, + const Dali::ImageDimensions& desiredSize, + const float& scaleFactor, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const bool& loadSynchronously, + const bool& cropToMask, + const UseAtlas& useAtlas, + const TextureHash& hash, + const bool& orientationCorrection, + const bool& preMultiplyOnLoad, + const Dali::AnimatedImageLoading& animatedImageLoading, + const std::uint32_t& frameIndex) + : url(url), + desiredSize(desiredSize), + useSize(desiredSize), + atlasRect(0.0f, 0.0f, 1.0f, 1.0f), // Full atlas rectangle + textureId(textureId), + maskTextureId(maskTextureId), + hash(hash), + scaleFactor(scaleFactor), + referenceCount(1u), + loadState(LoadState::NOT_STARTED), + fittingMode(fittingMode), + samplingMode(samplingMode), + storageType(StorageType::UPLOAD_TO_TEXTURE), + animatedImageLoading(animatedImageLoading), + frameIndex(frameIndex), + useAtlas(useAtlas), + loadSynchronously(loadSynchronously), + cropToMask(cropToMask), + orientationCorrection(true), + preMultiplyOnLoad(preMultiplyOnLoad), + preMultiplied(false) + { + } + + /** + * Container type used to store all observer clients of this Texture + */ + typedef Dali::Vector ObserverListType; + + ObserverListType observerList; ///< Container used to store all observer clients of this Texture + Dali::Toolkit::ImageAtlas atlas; ///< The atlas this Texture lays within (if any) + Dali::Devel::PixelBuffer pixelBuffer; ///< The PixelBuffer holding the image data (May be empty after upload) + Dali::TextureSet textureSet; ///< The TextureSet holding the Texture + VisualUrl url; ///< The URL of the image + Dali::ImageDimensions desiredSize; ///< The size requested + Dali::ImageDimensions useSize; ///< The size used + Dali::Vector4 atlasRect; ///< The atlas rect used if atlased + TextureId textureId; ///< The TextureId associated with this Texture + TextureId maskTextureId; ///< The mask TextureId to be applied on load + TextureHash hash; ///< The hash used to cache this Texture + float scaleFactor; ///< The scale factor to apply to the Texture when masking + std::int16_t referenceCount; ///< The reference count of clients using this Texture + LoadState loadState; ///< The load state showing the load progress of the Texture + Dali::FittingMode::Type fittingMode : 3; ///< The requested FittingMode + Dali::SamplingMode::Type samplingMode : 3; ///< The requested SamplingMode + StorageType storageType; ///< CPU storage / GPU upload; + Dali::AnimatedImageLoading animatedImageLoading; ///< AnimatedImageLoading that contains animated image information. + std::uint32_t frameIndex; ///< frame index that be loaded, in case of animated image + UseAtlas useAtlas; ///< USE_ATLAS if an atlas was requested. + + bool loadSynchronously : 1; ///< True if synchronous loading was requested + bool cropToMask : 1; ///< True if the image should be cropped to the mask size. + bool orientationCorrection : 1; ///< True if the image should be rotated to match exif orientation data + bool preMultiplyOnLoad : 1; ///< True if the image's color should be multiplied by it's alpha + bool preMultiplied : 1; ///< True if the image's color was multiplied by it's alpha +}; + +} // namespace TextureManagerType + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // DALI_TOOLKIT_TEXTURE_MANAGER_TYPE_H diff --git a/dali-toolkit/internal/visuals/texture-upload-observer.cpp b/dali-toolkit/internal/texture-manager/texture-upload-observer.cpp similarity index 89% rename from dali-toolkit/internal/visuals/texture-upload-observer.cpp rename to dali-toolkit/internal/texture-manager/texture-upload-observer.cpp index 6e18175..524ef67 100644 --- a/dali-toolkit/internal/visuals/texture-upload-observer.cpp +++ b/dali-toolkit/internal/texture-manager/texture-upload-observer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ #include "texture-upload-observer.h" // INTERNAL INCLUDES -#include +#include // for INVALUD_TEXTURE_ID namespace Dali { @@ -39,7 +39,7 @@ TextureUploadObserver::TextureInformation::TextureInformation(ReturnType returnT TextureUploadObserver::TextureInformation::TextureInformation(ReturnType returnType, Devel::PixelBuffer pixelBuffer, const std::string& url, bool preMultiplied) : returnType(returnType), - textureId(Internal::TextureManager::INVALID_TEXTURE_ID), + textureId(Internal::TextureManagerType::INVALID_TEXTURE_ID), textureSet(), useAtlasing(false), atlasRect(Vector4::ZERO), diff --git a/dali-toolkit/internal/visuals/texture-upload-observer.h b/dali-toolkit/internal/texture-manager/texture-upload-observer.h similarity index 78% rename from dali-toolkit/internal/visuals/texture-upload-observer.h rename to dali-toolkit/internal/texture-manager/texture-upload-observer.h index 4bb21e8..211cad7 100644 --- a/dali-toolkit/internal/visuals/texture-upload-observer.h +++ b/dali-toolkit/internal/texture-manager/texture-upload-observer.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_INTERNAL_TEXTURE_UPLOAD_OBSERVER_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,8 @@ // EXTERNAL INCLUDES #include -#include #include +#include // INTERNAL INCLUDES #include @@ -29,7 +29,6 @@ namespace Dali { - namespace Toolkit { /** @@ -56,14 +55,14 @@ public: TextureInformation(); - ReturnType returnType; ///< Returned Texture type. - int32_t textureId; ///< The textureId of the loaded texture in the TextureManager - TextureSet textureSet; ///< The TextureSet containing the Texture - bool useAtlasing; ///< True if atlasing was used (note: this may be different to what was requested) - const Vector4& atlasRect; ///< If using atlasing, this is the rectangle within the atlas to use. - bool preMultiplied; ///< True if the image had pre-multiplied alpha applied - Devel::PixelBuffer pixelBuffer; ///< The PixelBuffer of the loaded image. - std::string_view url; ///< The url address of the loaded image. + ReturnType returnType; ///< Returned Texture type. + int32_t textureId; ///< The textureId of the loaded texture in the TextureManager + TextureSet textureSet; ///< The TextureSet containing the Texture + bool useAtlasing; ///< True if atlasing was used (note: this may be different to what was requested) + const Vector4& atlasRect; ///< If using atlasing, this is the rectangle within the atlas to use. + bool preMultiplied; ///< True if the image had pre-multiplied alpha applied + Devel::PixelBuffer pixelBuffer; ///< The PixelBuffer of the loaded image. + std::string_view url; ///< The url address of the loaded image. }; public: diff --git a/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h b/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h index dc19937..6a2394a 100644 --- a/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h +++ b/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_INTERNAL_FIXED_IMAGE_CACHE_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,8 @@ */ // EXTERNAL INCLUDES +#include #include -#include namespace Dali { diff --git a/dali-toolkit/internal/visuals/animated-image/image-cache.h b/dali-toolkit/internal/visuals/animated-image/image-cache.h index f21d707..3200902 100644 --- a/dali-toolkit/internal/visuals/animated-image/image-cache.h +++ b/dali-toolkit/internal/visuals/animated-image/image-cache.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_INTERNAL_IMAGE_CACHE_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,8 @@ */ // EXTERNAL INCLUDES -#include -#include +#include +#include namespace Dali { diff --git a/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h b/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h index e1d201b..08aec52 100644 --- a/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h +++ b/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_INTERNAL_ROLLING_ANIMATED_IMAGE_CACHE_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,8 @@ */ // EXTERNAL INCLUDES +#include #include -#include #include #include diff --git a/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h b/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h index 1490c41..7647e31 100644 --- a/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h +++ b/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_INTERNAL_ROLLING_IMAGE_CACHE_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,9 +18,8 @@ */ // EXTERNAL INCLUDES - +#include #include -#include #include namespace Dali diff --git a/dali-toolkit/internal/visuals/image/image-visual.cpp b/dali-toolkit/internal/visuals/image/image-visual.cpp index c5344a4..263974f 100644 --- a/dali-toolkit/internal/visuals/image/image-visual.cpp +++ b/dali-toolkit/internal/visuals/image/image-visual.cpp @@ -31,9 +31,9 @@ // INTERNAL HEADERS #include +#include #include #include -#include #include #include #include diff --git a/dali-toolkit/internal/visuals/image/image-visual.h b/dali-toolkit/internal/visuals/image/image-visual.h index bc94ffb..bf9b64b 100644 --- a/dali-toolkit/internal/visuals/image/image-visual.h +++ b/dali-toolkit/internal/visuals/image/image-visual.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_INTERNAL_IMAGE_VISUAL_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ // INTERNAL INCLUDES #include #include -#include +#include #include #include #include diff --git a/dali-toolkit/internal/visuals/npatch-data.h b/dali-toolkit/internal/visuals/npatch-data.h index 94d7f2b..f67e599 100644 --- a/dali-toolkit/internal/visuals/npatch-data.h +++ b/dali-toolkit/internal/visuals/npatch-data.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_NPATCH_DATA_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ // INTERNAL INCLUDES #include -#include +#include #include namespace Dali diff --git a/dali-toolkit/internal/visuals/npatch-loader.h b/dali-toolkit/internal/visuals/npatch-loader.h index dd7f022..6475613 100644 --- a/dali-toolkit/internal/visuals/npatch-loader.h +++ b/dali-toolkit/internal/visuals/npatch-loader.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_NPATCH_LOADER_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,8 +25,8 @@ // INTERNAL INCLUDES #include +#include #include -#include #include namespace Dali diff --git a/dali-toolkit/internal/visuals/npatch/npatch-visual.h b/dali-toolkit/internal/visuals/npatch/npatch-visual.h index a266edc..ac66c45 100644 --- a/dali-toolkit/internal/visuals/npatch/npatch-visual.h +++ b/dali-toolkit/internal/visuals/npatch/npatch-visual.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_INTERNAL_N_PATCH_VISUAL_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ #include // INTERNAL INCLUDES -#include +#include #include #include #include diff --git a/dali-toolkit/internal/visuals/texture-manager-impl.cpp b/dali-toolkit/internal/visuals/texture-manager-impl.cpp deleted file mode 100644 index 3578ede..0000000 --- a/dali-toolkit/internal/visuals/texture-manager-impl.cpp +++ /dev/null @@ -1,1555 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// CLASS HEADER -#include - -// EXTERNAL HEADERS -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// INTERNAL HEADERS -#include -#include -#include -#include -#include - -namespace -{ -constexpr auto INITIAL_CACHE_NUMBER = size_t{0u}; -constexpr auto DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS = size_t{4u}; -constexpr auto DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS = size_t{8u}; - -constexpr auto NUMBER_OF_LOCAL_LOADER_THREADS_ENV = "DALI_TEXTURE_LOCAL_THREADS"; -constexpr auto NUMBER_OF_REMOTE_LOADER_THREADS_ENV = "DALI_TEXTURE_REMOTE_THREADS"; - -size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue) -{ - using Dali::EnvironmentVariable::GetEnvironmentVariable; - auto numberString = GetEnvironmentVariable(environmentVariable); - auto numberOfThreads = numberString ? std::strtoul(numberString, nullptr, 10) : 0; - constexpr auto MAX_NUMBER_OF_THREADS = 100u; - DALI_ASSERT_DEBUG(numberOfThreads < MAX_NUMBER_OF_THREADS); - return (numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS) ? numberOfThreads : defaultValue; -} - -size_t GetNumberOfLocalLoaderThreads() -{ - return GetNumberOfThreads(NUMBER_OF_LOCAL_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS); -} - -size_t GetNumberOfRemoteLoaderThreads() -{ - return GetNumberOfThreads(NUMBER_OF_REMOTE_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS); -} - -} // namespace - -namespace Dali -{ -namespace Toolkit -{ -namespace Internal -{ -namespace -{ -#ifdef DEBUG_ENABLED -Debug::Filter* gTextureManagerLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXTURE_MANAGER"); - -#define GET_LOAD_STATE_STRING(loadState) \ - loadState == TextureManager::LoadState::NOT_STARTED ? "NOT_STARTED" : loadState == TextureManager::LoadState::LOADING ? "LOADING" \ - : loadState == TextureManager::LoadState::LOAD_FINISHED ? "LOAD_FINISHED" \ - : loadState == TextureManager::LoadState::WAITING_FOR_MASK ? "WAITING_FOR_MASK" \ - : loadState == TextureManager::LoadState::MASK_APPLYING ? "MASK_APPLYING" \ - : loadState == TextureManager::LoadState::MASK_APPLIED ? "MASK_APPLIED" \ - : loadState == TextureManager::LoadState::UPLOADED ? "UPLOADED" \ - : loadState == TextureManager::LoadState::CANCELLED ? "CANCELLED" \ - : loadState == TextureManager::LoadState::LOAD_FAILED ? "LOAD_FAILED" \ - : "Unknown" - -#endif - -const uint32_t DEFAULT_ATLAS_SIZE(1024u); ///< This size can fit 8 by 8 images of average size 128 * 128 -const Vector4 FULL_ATLAS_RECT(0.0f, 0.0f, 1.0f, 1.0f); ///< UV Rectangle that covers the full Texture -const int INVALID_INDEX(-1); ///< Invalid index used to represent a non-existant TextureInfo struct -const int INVALID_CACHE_INDEX(-1); ///< Invalid Cache index - -void PreMultiply(Devel::PixelBuffer pixelBuffer, TextureManager::MultiplyOnLoad& preMultiplyOnLoad) -{ - if(Pixel::HasAlpha(pixelBuffer.GetPixelFormat())) - { - if(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD) - { - pixelBuffer.MultiplyColorByAlpha(); - } - } - else - { - preMultiplyOnLoad = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - } -} - -} // Anonymous namespace - -TextureManager::MaskingData::MaskingData() -: mAlphaMaskUrl(), - mAlphaMaskId(INVALID_TEXTURE_ID), - mContentScaleFactor(1.0f), - mCropToMask(true) -{ -} - -TextureManager::TextureManager() -: mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]() - { return AsyncLoadingHelper(*this); }), - mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]() - { return AsyncLoadingHelper(*this); }), - mExternalTextures(), - mLifecycleObservers(), - mLoadQueue(), - mCurrentTextureId(0), - mQueueLoadFlag(false) -{ - // Initialize the AddOn - RenderingAddOn::Get(); -} - -TextureManager::~TextureManager() -{ - for(auto iter = mLifecycleObservers.Begin(), endIter = mLifecycleObservers.End(); iter != endIter; ++iter) - { - (*iter)->TextureManagerDestroyed(); - } -} - -TextureSet TextureManager::LoadAnimatedImageTexture( - Dali::AnimatedImageLoading animatedImageLoading, uint32_t frameIndex, Dali::SamplingMode::Type samplingMode, bool synchronousLoading, TextureManager::TextureId& textureId, Dali::WrapMode::Type wrapModeU, Dali::WrapMode::Type wrapModeV, TextureUploadObserver* textureObserver) -{ - TextureSet textureSet; - - if(synchronousLoading) - { - Devel::PixelBuffer pixelBuffer; - if(animatedImageLoading) - { - pixelBuffer = animatedImageLoading.LoadFrame(frameIndex); - } - if(!pixelBuffer) - { - DALI_LOG_ERROR("TextureManager::LoadAnimatedImageTexture: Synchronous loading is failed\n"); - } - else - { - PixelData pixelData = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer - if(!textureSet) - { - Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight()); - texture.Upload(pixelData); - textureSet = TextureSet::New(); - textureSet.SetTexture(0u, texture); - } - } - } - 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, false); - TextureManager::LoadState loadState = GetTextureStateInternal(textureId); - if(loadState == TextureManager::LoadState::UPLOADED) - { - // LoadComplete has already been called - keep the same texture set - textureSet = GetTextureSet(textureId); - } - } - - if(textureSet) - { - Sampler sampler = Sampler::New(); - sampler.SetWrapMode(wrapModeU, wrapModeV); - textureSet.SetSampler(0u, sampler); - } - - return textureSet; -} - -Devel::PixelBuffer TextureManager::LoadPixelBuffer( - const VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode, bool synchronousLoading, TextureUploadObserver* textureObserver, bool orientationCorrection, TextureManager::MultiplyOnLoad& preMultiplyOnLoad) -{ - Devel::PixelBuffer pixelBuffer; - if(synchronousLoading) - { - if(url.IsValid()) - { - 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); - } - if(pixelBuffer && preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD) - { - PreMultiply(pixelBuffer, preMultiplyOnLoad); - } - } - } - 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, false); - } - - return pixelBuffer; -} - -TextureSet TextureManager::LoadTexture( - const VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode, MaskingDataPointer& maskInfo, bool synchronousLoading, TextureManager::TextureId& textureId, Vector4& textureRect, Dali::ImageDimensions& textureRectSize, bool& atlasingStatus, bool& loadingStatus, Dali::WrapMode::Type wrapModeU, Dali::WrapMode::Type wrapModeV, TextureUploadObserver* textureObserver, AtlasUploadObserver* atlasObserver, ImageAtlasManagerPtr imageAtlasManager, bool orientationCorrection, TextureManager::ReloadPolicy reloadPolicy, TextureManager::MultiplyOnLoad& preMultiplyOnLoad) -{ - TextureSet textureSet; - - loadingStatus = false; - textureRect = FULL_ATLAS_RECT; - - if(VisualUrl::TEXTURE == url.GetProtocolType()) - { - std::string location = url.GetLocation(); - if(location.size() > 0u) - { - TextureId id = std::stoi(location); - for(auto&& elem : mExternalTextures) - { - if(elem.textureId == id) - { - preMultiplyOnLoad = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - textureId = elem.textureId; - return elem.textureSet; - } - } - } - } - else - { - // For Atlas - if(synchronousLoading && atlasingStatus && imageAtlasManager->CheckAtlasAvailable(url, desiredSize)) - { - Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection); - - if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid()) - { - 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(!textureSet) - { - atlasingStatus = false; - } - } - - if(!textureSet) - { - loadingStatus = true; - if(atlasingStatus) - { - textureSet = imageAtlasManager->Add(textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver); - } - if(!textureSet) // big image, no atlasing or atlasing failed - { - 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) - { - // LoadComplete has already been called - keep the same texture set - textureSet = GetTextureSet(textureId); - } - - // 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; - } - } - } - - if(!atlasingStatus && textureSet) - { - Sampler sampler = Sampler::New(); - sampler.SetWrapMode(wrapModeU, wrapModeV); - textureSet.SetSampler(0u, sampler); - } - - if(synchronousLoading) - { - loadingStatus = false; - } - - return textureSet; -} - -TextureManager::TextureId TextureManager::RequestLoad( - const VisualUrl& url, - const ImageDimensions desiredSize, - FittingMode::Type fittingMode, - Dali::SamplingMode::Type samplingMode, - const UseAtlas useAtlas, - TextureUploadObserver* observer, - bool orientationCorrection, - TextureManager::ReloadPolicy reloadPolicy, - 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, synchronousLoading); -} - -TextureManager::TextureId TextureManager::RequestLoad( - const VisualUrl& url, - TextureId maskTextureId, - float contentScale, - const ImageDimensions desiredSize, - FittingMode::Type fittingMode, - Dali::SamplingMode::Type samplingMode, - const UseAtlas useAtlas, - bool cropToMask, - TextureUploadObserver* observer, - bool orientationCorrection, - TextureManager::ReloadPolicy reloadPolicy, - 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, synchronousLoading); -} - -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, synchronousLoading); -} - -TextureManager::TextureId TextureManager::RequestLoadInternal( - const VisualUrl& url, - TextureId maskTextureId, - float contentScale, - const ImageDimensions desiredSize, - FittingMode::Type fittingMode, - Dali::SamplingMode::Type samplingMode, - UseAtlas useAtlas, - bool cropToMask, - StorageType storageType, - TextureUploadObserver* observer, - bool orientationCorrection, - TextureManager::ReloadPolicy reloadPolicy, - TextureManager::MultiplyOnLoad& preMultiplyOnLoad, - Dali::AnimatedImageLoading animatedImageLoading, - uint32_t frameIndex, - bool synchronousLoading) -{ - // First check if the requested Texture is cached. - bool isAnimatedImage = (animatedImageLoading) ? true : false; - - TextureHash textureHash = INITIAL_CACHE_NUMBER; - int cacheIndex = INVALID_CACHE_INDEX; - if(storageType != StorageType::RETURN_PIXEL_BUFFER && !isAnimatedImage) - { - textureHash = GenerateHash(url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId); - - // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision. - cacheIndex = FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad); - } - - TextureManager::TextureId textureId = INVALID_TEXTURE_ID; - // Check if the requested Texture exists in the cache. - if(cacheIndex != INVALID_CACHE_INDEX) - { - if(TextureManager::ReloadPolicy::CACHED == reloadPolicy) - { - // Mark this texture being used by another client resource. Forced reload would replace the current texture - // without the need for incrementing the reference count. - ++(mTextureInfoContainer[cacheIndex].referenceCount); - } - textureId = mTextureInfoContainer[cacheIndex].textureId; - - // Update preMultiplyOnLoad value. It should be changed according to preMultiplied value of the cached info. - preMultiplyOnLoad = mTextureInfoContainer[cacheIndex].preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - - 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); - } - - if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required - { - if(VisualUrl::BUFFER == url.GetProtocolType()) - { - std::string location = url.GetLocation(); - if(location.size() > 0u) - { - TextureId targetId = std::stoi(location); - const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(targetId); - if(encodedImageBuffer) - { - textureId = targetId; - - // Increase EncodedImageBuffer reference during it contains mTextureInfoContainer. - UseExternalResource(url.GetUrl()); - } - } - } - - 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; - - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId); - } - - // The below code path is common whether we are using the cache or not. - // The textureInfoIndex now refers to either a pre-existing cached TextureInfo, - // or a new TextureInfo just created. - TextureInfo& textureInfo(mTextureInfoContainer[cacheIndex]); - textureInfo.maskTextureId = maskTextureId; - textureInfo.storageType = storageType; - textureInfo.orientationCorrection = orientationCorrection; - - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureInfo loadState:%s\n", GET_LOAD_STATE_STRING(textureInfo.loadState)); - - // Force reloading of texture by setting loadState unless already loading or cancelled. - if(TextureManager::ReloadPolicy::FORCED == reloadPolicy && - TextureManager::LoadState::LOADING != textureInfo.loadState && - TextureManager::LoadState::WAITING_FOR_MASK != textureInfo.loadState && - TextureManager::LoadState::MASK_APPLYING != textureInfo.loadState && - TextureManager::LoadState::MASK_APPLIED != textureInfo.loadState && - TextureManager::LoadState::CANCELLED != textureInfo.loadState) - { - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId); - - textureInfo.loadState = TextureManager::LoadState::NOT_STARTED; - } - - if(!synchronousLoading) - { - // 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) - { - 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: - { - 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; - } - } - } - 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) - { - return textureId; - } - else - { - Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection);; - - if(!pixelBuffer) - { - // 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); - } - } - } - - // Return the TextureId for which this Texture can now be referenced by externally. - return textureId; -} - -void TextureManager::Remove(const TextureManager::TextureId textureId, TextureUploadObserver* observer) -{ - int textureInfoIndex = GetCacheIndexFromId(textureId); - - if(textureInfoIndex != INVALID_INDEX) - { - TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex]); - - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::Remove(%d) url:%s\n cacheIdx:%d loadState:%s reference count = %d\n", textureId, textureInfo.url.GetUrl().c_str(), textureInfoIndex, GET_LOAD_STATE_STRING(textureInfo.loadState), textureInfo.referenceCount); - - // Decrement the reference count and check if this is the last user of this Texture. - if(--textureInfo.referenceCount <= 0) - { - // This is the last remove for this Texture. - textureInfo.referenceCount = 0; - bool removeTextureInfo = false; - - // If loaded, we can remove the TextureInfo and the Atlas (if atlased). - if(textureInfo.loadState == LoadState::UPLOADED) - { - if(textureInfo.atlas) - { - textureInfo.atlas.Remove(textureInfo.atlasRect); - } - removeTextureInfo = true; - } - else if(textureInfo.loadState == LoadState::LOADING) - { - // We mark the textureInfo for removal. - // Once the load has completed, this method will be called again. - textureInfo.loadState = LoadState::CANCELLED; - } - else - { - // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data. - removeTextureInfo = true; - } - - // If the state allows us to remove the TextureInfo data, we do so. - if(removeTextureInfo) - { - // If url location is BUFFER, decrease reference count of EncodedImageBuffer. - if(textureInfo.url.IsBufferResource()) - { - RemoveExternalEncodedImageBuffer(textureInfo.url.GetUrl()); - } - // Permanently remove the textureInfo struct. - mTextureInfoContainer.erase(mTextureInfoContainer.begin() + textureInfoIndex); - } - } - - if(observer) - { - // Remove element from the LoadQueue - for(auto&& element : mLoadQueue) - { - if(element.mObserver == observer) - { - // Do not erase the item. We will clear it later in ProcessQueuedTextures(). - element.mObserver = nullptr; - break; - } - } - } - } -} - -VisualUrl TextureManager::GetVisualUrl(TextureId textureId) -{ - VisualUrl visualUrl(""); - int cacheIndex = GetCacheIndexFromId(textureId); - - if(cacheIndex != INVALID_CACHE_INDEX) - { - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::GetVisualUrl. Using cached texture id=%d, textureId=%d\n", cacheIndex, textureId); - - TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); - visualUrl = cachedTextureInfo.url; - } - return visualUrl; -} - -TextureManager::LoadState TextureManager::GetTextureState(TextureId textureId) -{ - LoadState loadState = TextureManager::LoadState::NOT_STARTED; - - int cacheIndex = GetCacheIndexFromId(textureId); - if(cacheIndex != INVALID_CACHE_INDEX) - { - TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); - loadState = cachedTextureInfo.loadState; - } - else - { - for(auto&& elem : mExternalTextures) - { - if(elem.textureId == textureId) - { - loadState = LoadState::UPLOADED; - break; - } - } - } - return loadState; -} - -TextureManager::LoadState TextureManager::GetTextureStateInternal(TextureId textureId) -{ - LoadState loadState = TextureManager::LoadState::NOT_STARTED; - - int cacheIndex = GetCacheIndexFromId(textureId); - if(cacheIndex != INVALID_CACHE_INDEX) - { - TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); - loadState = cachedTextureInfo.loadState; - } - - 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 - - int cacheIndex = GetCacheIndexFromId(textureId); - if(cacheIndex != INVALID_CACHE_INDEX) - { - TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); - textureSet = cachedTextureInfo.textureSet; - } - else - { - for(auto&& elem : mExternalTextures) - { - if(elem.textureId == textureId) - { - textureSet = elem.textureSet; - break; - } - } - } - return textureSet; -} - -EncodedImageBuffer TextureManager::GetEncodedImageBuffer(TextureId textureId) -{ - EncodedImageBuffer encodedImageBuffer; // empty handle - for(auto&& elem : mEncodedBufferTextures) - { - if(elem.textureId == textureId) - { - encodedImageBuffer = elem.encodedImageBuffer; - break; - } - } - return encodedImageBuffer; -} - -EncodedImageBuffer TextureManager::GetEncodedImageBuffer(const std::string& url) -{ - EncodedImageBuffer encodedImageBuffer; // empty handle - if(url.size() > 0 && VisualUrl::BUFFER == VisualUrl::GetProtocolType(url)) - { - std::string location = VisualUrl::GetLocation(url); - if(location.size() > 0u) - { - TextureId targetId = std::stoi(location); - return GetEncodedImageBuffer(targetId); - } - } - return encodedImageBuffer; -} - -std::string TextureManager::AddExternalTexture(TextureSet& textureSet) -{ - TextureManager::ExternalTextureInfo info; - info.textureId = GenerateUniqueTextureId(); - info.textureSet = textureSet; - mExternalTextures.emplace_back(info); - - return VisualUrl::CreateTextureUrl(std::to_string(info.textureId)); -} - -std::string TextureManager::AddExternalEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer) -{ - // Duplication check - for(auto&& elem : mEncodedBufferTextures) - { - if(elem.encodedImageBuffer == encodedImageBuffer) - { - // If same buffer added, increase reference count and return. - elem.referenceCount++; - return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId)); - } - } - TextureManager::EncodedBufferTextureInfo info(GenerateUniqueTextureId(), encodedImageBuffer); - mEncodedBufferTextures.emplace_back(info); - return VisualUrl::CreateBufferUrl(std::to_string(info.textureId)); -} - -TextureSet TextureManager::RemoveExternalTexture(const std::string& url) -{ - if(url.size() > 0u) - { - if(VisualUrl::TEXTURE == VisualUrl::GetProtocolType(url)) - { - // get the location from the Url - std::string location = VisualUrl::GetLocation(url); - if(location.size() > 0u) - { - TextureId id = std::stoi(location); - const auto end = mExternalTextures.end(); - for(auto iter = mExternalTextures.begin(); iter != end; ++iter) - { - if(iter->textureId == id) - { - auto textureSet = iter->textureSet; - if(--(iter->referenceCount) <= 0) - { - mExternalTextures.erase(iter); - } - return textureSet; - } - } - } - } - } - return TextureSet(); -} - -EncodedImageBuffer TextureManager::RemoveExternalEncodedImageBuffer(const std::string& url) -{ - if(url.size() > 0u) - { - if(VisualUrl::BUFFER == VisualUrl::GetProtocolType(url)) - { - // get the location from the Url - std::string location = VisualUrl::GetLocation(url); - if(location.size() > 0u) - { - TextureId id = std::stoi(location); - const auto end = mEncodedBufferTextures.end(); - for(auto iter = mEncodedBufferTextures.begin(); iter != end; ++iter) - { - if(iter->textureId == id) - { - auto encodedImageBuffer = iter->encodedImageBuffer; - if(--(iter->referenceCount) <= 0) - { - mEncodedBufferTextures.erase(iter); - } - return encodedImageBuffer; - } - } - } - } - } - return EncodedImageBuffer(); -} - -void TextureManager::UseExternalResource(const VisualUrl& url) -{ - if(VisualUrl::TEXTURE == url.GetProtocolType()) - { - std::string location = url.GetLocation(); - if(location.size() > 0u) - { - TextureId id = std::stoi(location); - for(auto&& elem : mExternalTextures) - { - if(elem.textureId == id) - { - elem.referenceCount++; - return; - } - } - } - } - else if(VisualUrl::BUFFER == url.GetProtocolType()) - { - std::string location = url.GetLocation(); - if(location.size() > 0u) - { - TextureId id = std::stoi(location); - for(auto&& elem : mEncodedBufferTextures) - { - if(elem.textureId == id) - { - elem.referenceCount++; - return; - } - } - } - } -} - -void TextureManager::AddObserver(TextureManager::LifecycleObserver& observer) -{ - // make sure an observer doesn't observe the same object twice - // otherwise it will get multiple calls to ObjectDestroyed() - DALI_ASSERT_DEBUG(mLifecycleObservers.End() == std::find(mLifecycleObservers.Begin(), mLifecycleObservers.End(), &observer)); - mLifecycleObservers.PushBack(&observer); -} - -void TextureManager::RemoveObserver(TextureManager::LifecycleObserver& observer) -{ - // Find the observer... - auto endIter = mLifecycleObservers.End(); - for(auto iter = mLifecycleObservers.Begin(); iter != endIter; ++iter) - { - if((*iter) == &observer) - { - mLifecycleObservers.Erase(iter); - break; - } - } - DALI_ASSERT_DEBUG(endIter != mLifecycleObservers.End()); -} - -void TextureManager::LoadOrQueueTexture(TextureInfo& textureInfo, TextureUploadObserver* observer) -{ - switch(textureInfo.loadState) - { - case LoadState::NOT_STARTED: - case LoadState::LOAD_FAILED: - { - if(mQueueLoadFlag) - { - QueueLoadTexture(textureInfo, observer); - } - else - { - LoadTexture(textureInfo, observer); - } - break; - } - case LoadState::UPLOADED: - { - if(mQueueLoadFlag) - { - QueueLoadTexture(textureInfo, observer); - } - else - { - // The Texture has already loaded. The other observers have already been notified. - // We need to send a "late" loaded notification for this observer. - observer->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, textureInfo.textureId, textureInfo.textureSet, textureInfo.useAtlas, textureInfo.atlasRect, textureInfo.preMultiplied)); - } - break; - } - case LoadState::LOADING: - case LoadState::CANCELLED: - case LoadState::LOAD_FINISHED: - case LoadState::WAITING_FOR_MASK: - case LoadState::MASK_APPLYING: - case LoadState::MASK_APPLIED: - { - break; - } - } -} - -void TextureManager::QueueLoadTexture(TextureInfo& textureInfo, TextureUploadObserver* observer) -{ - auto textureId = textureInfo.textureId; - mLoadQueue.PushBack(LoadQueueElement(textureId, observer)); - - observer->DestructionSignal().Connect(this, &TextureManager::ObserverDestroyed); -} - -void TextureManager::LoadTexture(TextureInfo& textureInfo, TextureUploadObserver* observer) -{ - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::LoadTexture(): url:%s sync:%s\n", textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F"); - - textureInfo.loadState = LoadState::LOADING; - if(!textureInfo.loadSynchronously) - { - auto& loadersContainer = (textureInfo.url.IsLocalResource() || textureInfo.url.IsBufferResource()) ? mAsyncLocalLoaders : mAsyncRemoteLoaders; - auto loadingHelperIt = loadersContainer.GetNext(); - auto premultiplyOnLoad = (textureInfo.preMultiplyOnLoad && textureInfo.maskTextureId == INVALID_TEXTURE_ID) ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF; - DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End()); - if(textureInfo.animatedImageLoading) - { - loadingHelperIt->LoadAnimatedImage(textureInfo.textureId, textureInfo.animatedImageLoading, textureInfo.frameIndex); - } - else - { - loadingHelperIt->Load(textureInfo.textureId, textureInfo.url, textureInfo.desiredSize, textureInfo.fittingMode, textureInfo.samplingMode, textureInfo.orientationCorrection, premultiplyOnLoad); - } - } - ObserveTexture(textureInfo, observer); -} - -void TextureManager::ProcessQueuedTextures() -{ - for(auto&& element : mLoadQueue) - { - if(!element.mObserver) - { - continue; - } - - int cacheIndex = GetCacheIndexFromId(element.mTextureId); - if(cacheIndex != INVALID_CACHE_INDEX) - { - TextureInfo& textureInfo(mTextureInfoContainer[cacheIndex]); - if(textureInfo.loadState == LoadState::UPLOADED) - { - element.mObserver->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, textureInfo.textureId, textureInfo.textureSet, textureInfo.useAtlas, textureInfo.atlasRect, textureInfo.preMultiplied)); - } - else if(textureInfo.loadState == LoadState::LOAD_FINISHED && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER) - { - element.mObserver->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::PIXEL_BUFFER, textureInfo.pixelBuffer, textureInfo.url.GetUrl(), textureInfo.preMultiplied)); - } - else - { - LoadTexture(textureInfo, element.mObserver); - } - } - } - mLoadQueue.Clear(); -} - -void TextureManager::ObserveTexture(TextureInfo& textureInfo, - TextureUploadObserver* observer) -{ - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::ObserveTexture(): url:%s observer:%p\n", textureInfo.url.GetUrl().c_str(), observer); - - if(observer) - { - textureInfo.observerList.PushBack(observer); - observer->DestructionSignal().Connect(this, &TextureManager::ObserverDestroyed); - } -} - -void TextureManager::AsyncLoadComplete(AsyncLoadingInfoContainerType& loadingContainer, uint32_t id, Devel::PixelBuffer pixelBuffer) -{ - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( id:%d )\n", id); - - if(loadingContainer.size() >= 1u) - { - AsyncLoadingInfo loadingInfo = loadingContainer.front(); - - if(loadingInfo.loadId == id) - { - int cacheIndex = GetCacheIndexFromId(loadingInfo.textureId); - if(cacheIndex != INVALID_CACHE_INDEX) - { - TextureInfo& textureInfo(mTextureInfoContainer[cacheIndex]); - - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, " textureId:%d Url:%s CacheIndex:%d LoadState: %d\n", textureInfo.textureId, textureInfo.url.GetUrl().c_str(), cacheIndex, textureInfo.loadState); - - if(textureInfo.loadState != LoadState::CANCELLED) - { - // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified) - PostLoad(textureInfo, pixelBuffer); - } - else - { - Remove(textureInfo.textureId, nullptr); - } - } - } - - loadingContainer.pop_front(); - } -} - -void TextureManager::PostLoad(TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer) -{ - // Was the load successful? - if(pixelBuffer && (pixelBuffer.GetWidth() != 0) && (pixelBuffer.GetHeight() != 0)) - { - // No atlas support for now - textureInfo.useAtlas = NO_ATLAS; - textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied(); - - if(textureInfo.storageType == StorageType::UPLOAD_TO_TEXTURE) - { - // 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. - // 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) - { - textureInfo.loadState = LoadState::MASK_APPLIED; - UploadTexture(pixelBuffer, textureInfo); - NotifyObservers(textureInfo, true); - } - else - { - LoadState maskLoadState = GetTextureStateInternal(textureInfo.maskTextureId); - textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily - if(maskLoadState == LoadState::LOADING) - { - textureInfo.loadState = LoadState::WAITING_FOR_MASK; - } - else if(maskLoadState == LoadState::LOAD_FINISHED) - { - // Send New Task to Thread - ApplyMask(textureInfo, textureInfo.maskTextureId); - } - } - } - else - { - UploadTexture(pixelBuffer, textureInfo); - NotifyObservers(textureInfo, true); - } - } - else - { - textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data - textureInfo.loadState = LoadState::LOAD_FINISHED; - - if(textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER) - { - NotifyObservers(textureInfo, true); - } - else - { - // Check if there was another texture waiting for this load to complete - // (e.g. if this was an image mask, and its load is on a different thread) - CheckForWaitingTexture(textureInfo); - } - } - } - else - { - textureInfo.loadState = LoadState::LOAD_FAILED; - CheckForWaitingTexture(textureInfo); - NotifyObservers(textureInfo, false); - } -} - -void TextureManager::CheckForWaitingTexture(TextureInfo& maskTextureInfo) -{ - // Search the cache, checking if any texture has this texture id as a - // maskTextureId: - const unsigned int size = mTextureInfoContainer.size(); - - for(unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex) - { - if(mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId && - mTextureInfoContainer[cacheIndex].loadState == LoadState::WAITING_FOR_MASK) - { - TextureInfo& textureInfo(mTextureInfoContainer[cacheIndex]); - - if(maskTextureInfo.loadState == LoadState::LOAD_FINISHED) - { - // Send New Task to Thread - ApplyMask(textureInfo, maskTextureInfo.textureId); - } - else - { - textureInfo.pixelBuffer.Reset(); - textureInfo.loadState = LoadState::LOAD_FAILED; - NotifyObservers(textureInfo, false); - } - } - } -} - -void TextureManager::ApplyMask(TextureInfo& textureInfo, TextureId maskTextureId) -{ - int maskCacheIndex = GetCacheIndexFromId(maskTextureId); - if(maskCacheIndex != INVALID_CACHE_INDEX) - { - Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer; - Devel::PixelBuffer pixelBuffer = textureInfo.pixelBuffer; - textureInfo.pixelBuffer.Reset(); - - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::ApplyMask(): url:%s sync:%s\n", textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F"); - - textureInfo.loadState = LoadState::MASK_APPLYING; - auto& loadersContainer = (textureInfo.url.IsLocalResource() || textureInfo.url.IsBufferResource()) ? mAsyncLocalLoaders : mAsyncRemoteLoaders; - auto loadingHelperIt = loadersContainer.GetNext(); - auto premultiplyOnLoad = textureInfo.preMultiplyOnLoad ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF; - DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End()); - loadingHelperIt->ApplyMask(textureInfo.textureId, pixelBuffer, maskPixelBuffer, textureInfo.scaleFactor, textureInfo.cropToMask, premultiplyOnLoad); - } -} - -void TextureManager::UploadTexture(Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo) -{ - 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); - - // Check if this pixelBuffer is premultiplied - textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied(); - - auto& renderingAddOn = RenderingAddOn::Get(); - if(renderingAddOn.IsValid()) - { - renderingAddOn.CreateGeometry(textureInfo.textureId, pixelBuffer); - } - - Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight()); - - PixelData pixelData = Devel::PixelBuffer::Convert(pixelBuffer); - texture.Upload(pixelData); - if(!textureInfo.textureSet) - { - textureInfo.textureSet = TextureSet::New(); - } - textureInfo.textureSet.SetTexture(0u, texture); - } - - // Update the load state. - // Note: This is regardless of success as we care about whether a - // load attempt is in progress or not. If unsuccessful, a broken - // image is still loaded. - textureInfo.loadState = LoadState::UPLOADED; -} - -void TextureManager::NotifyObservers(TextureInfo& textureInfo, bool success) -{ - TextureId textureId = textureInfo.textureId; - - // If there is an observer: Notify the load is complete, whether successful or not, - // and erase it from the list - TextureInfo* info = &textureInfo; - - mQueueLoadFlag = true; - - while(info->observerList.Count()) - { - TextureUploadObserver* observer = info->observerList[0]; - - // During LoadComplete() a Control ResourceReady() signal is emitted. - // During that signal the app may add remove /add Textures (e.g. via - // ImageViews). - // It is possible for observers to be removed from the observer list, - // and it is also possible for the mTextureInfoContainer to be modified, - // invalidating the reference to the textureInfo struct. - // Texture load requests for the same URL are deferred until the end of this - // method. - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "NotifyObservers() url:%s loadState:%s\n", textureInfo.url.GetUrl().c_str(), GET_LOAD_STATE_STRING(textureInfo.loadState)); - - // It is possible for the observer to be deleted. - // Disconnect and remove the observer first. - observer->DestructionSignal().Disconnect(this, &TextureManager::ObserverDestroyed); - - info->observerList.Erase(info->observerList.begin()); - - if(info->storageType == StorageType::RETURN_PIXEL_BUFFER) - { - observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::PIXEL_BUFFER, info->pixelBuffer, info->url.GetUrl(), info->preMultiplied)); - } - else - { - observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, info->textureId, info->textureSet, info->useAtlas, info->atlasRect, info->preMultiplied)); - } - - // Get the textureInfo from the container again as it may have been invalidated. - int textureInfoIndex = GetCacheIndexFromId(textureId); - if(textureInfoIndex == INVALID_CACHE_INDEX) - { - break; // texture has been removed - can stop. - } - info = &mTextureInfoContainer[textureInfoIndex]; - } - - mQueueLoadFlag = false; - ProcessQueuedTextures(); - - if(info->storageType == StorageType::RETURN_PIXEL_BUFFER && info->observerList.Count() == 0) - { - Remove(info->textureId, nullptr); - } -} - -TextureManager::TextureId TextureManager::GenerateUniqueTextureId() -{ - return mCurrentTextureId++; -} - -int TextureManager::GetCacheIndexFromId(const TextureId textureId) -{ - const unsigned int size = mTextureInfoContainer.size(); - - for(unsigned int i = 0; i < size; ++i) - { - if(mTextureInfoContainer[i].textureId == textureId) - { - return i; - } - } - - return INVALID_CACHE_INDEX; -} - -TextureManager::TextureHash TextureManager::GenerateHash( - const std::string& url, - const ImageDimensions size, - const FittingMode::Type fittingMode, - const Dali::SamplingMode::Type samplingMode, - const UseAtlas useAtlas, - TextureId maskTextureId) -{ - std::string hashTarget(url); - const size_t urlLength = hashTarget.length(); - const uint16_t width = size.GetWidth(); - const uint16_t height = size.GetWidth(); - - // If either the width or height has been specified, include the resizing options in the hash - if(width != 0 || height != 0) - { - // We are appending 5 bytes to the URL to form the hash input. - hashTarget.resize(urlLength + 5u); - char* hashTargetPtr = &(hashTarget[urlLength]); - - // Pack the width and height (4 bytes total). - *hashTargetPtr++ = size.GetWidth() & 0xff; - *hashTargetPtr++ = (size.GetWidth() >> 8u) & 0xff; - *hashTargetPtr++ = size.GetHeight() & 0xff; - *hashTargetPtr++ = (size.GetHeight() >> 8u) & 0xff; - - // Bit-pack the FittingMode, SamplingMode and atlasing. - // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit - *hashTargetPtr = (fittingMode << 4u) | (samplingMode << 1) | useAtlas; - } - else - { - // We are not including sizing information, but we still need an extra byte for atlasing. - hashTarget.resize(urlLength + 1u); - - // Add the atlasing to the hash input. - switch(useAtlas) - { - case UseAtlas::NO_ATLAS: - { - hashTarget[urlLength] = 'f'; - break; - } - case UseAtlas::USE_ATLAS: - { - hashTarget[urlLength] = 't'; - break; - } - } - } - - if(maskTextureId != INVALID_TEXTURE_ID) - { - auto textureIdIndex = hashTarget.length(); - hashTarget.resize(hashTarget.length() + sizeof(TextureId)); - unsigned char* hashTargetPtr = reinterpret_cast(&(hashTarget[textureIdIndex])); - - // Append the texture id to the end of the URL byte by byte: - // (to avoid SIGBUS / alignment issues) - for(size_t byteIter = 0; byteIter < sizeof(TextureId); ++byteIter) - { - *hashTargetPtr++ = maskTextureId & 0xff; - maskTextureId >>= 8u; - } - } - - return Dali::CalculateHash(hashTarget); -} - -int TextureManager::FindCachedTexture( - const TextureManager::TextureHash hash, - const std::string& url, - const ImageDimensions size, - const FittingMode::Type fittingMode, - const Dali::SamplingMode::Type samplingMode, - const bool useAtlas, - TextureId maskTextureId, - TextureManager::MultiplyOnLoad preMultiplyOnLoad) -{ - // Default to an invalid ID, in case we do not find a match. - int cacheIndex = INVALID_CACHE_INDEX; - - // Iterate through our hashes to find a match. - const unsigned int count = mTextureInfoContainer.size(); - for(unsigned int i = 0u; i < count; ++i) - { - if(mTextureInfoContainer[i].hash == hash) - { - // We have a match, now we check all the original parameters in case of a hash collision. - TextureInfo& textureInfo(mTextureInfoContainer[i]); - - if((url == textureInfo.url.GetUrl()) && - (useAtlas == textureInfo.useAtlas) && - (maskTextureId == textureInfo.maskTextureId) && - (size == textureInfo.desiredSize) && - ((size.GetWidth() == 0 && size.GetHeight() == 0) || - (fittingMode == textureInfo.fittingMode && - samplingMode == textureInfo.samplingMode))) - { - // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different. - // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false. - if((preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad) || (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied)) - { - // The found Texture is a match. - cacheIndex = i; - break; - } - } - } - } - - return cacheIndex; -} - -void TextureManager::ObserverDestroyed(TextureUploadObserver* observer) -{ - const unsigned int count = mTextureInfoContainer.size(); - for(unsigned int i = 0; i < count; ++i) - { - TextureInfo& textureInfo(mTextureInfoContainer[i]); - for(TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin(); - j != textureInfo.observerList.End();) - { - if(*j == observer) - { - j = textureInfo.observerList.Erase(j); - } - else - { - ++j; - } - } - } - - // Remove element from the LoadQueue - for(auto&& element : mLoadQueue) - { - if(element.mObserver == observer) - { - element.mObserver = nullptr; - } - } -} - -TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(TextureManager& textureManager) -: AsyncLoadingHelper(Toolkit::AsyncImageLoader::New(), textureManager, AsyncLoadingInfoContainerType()) -{ -} - -void TextureManager::AsyncLoadingHelper::LoadAnimatedImage(TextureId textureId, - Dali::AnimatedImageLoading animatedImageLoading, - uint32_t frameIndex) -{ - mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId)); - auto id = GetImplementation(mLoader).LoadAnimatedImage(animatedImageLoading, frameIndex); - mLoadingInfoContainer.back().loadId = id; -} - -void TextureManager::AsyncLoadingHelper::Load(TextureId textureId, - const VisualUrl& url, - ImageDimensions desiredSize, - FittingMode::Type fittingMode, - SamplingMode::Type samplingMode, - bool orientationCorrection, - DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad) -{ - mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId)); - if(DALI_UNLIKELY(url.IsBufferResource())) - { - auto id = GetImplementation(mLoader).LoadEncodedImageBuffer(mTextureManager.GetEncodedImageBuffer(url.GetUrl()), desiredSize, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad); - mLoadingInfoContainer.back().loadId = id; - } - else - { - auto id = GetImplementation(mLoader).Load(url, desiredSize, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad); - mLoadingInfoContainer.back().loadId = id; - } -} - -void TextureManager::AsyncLoadingHelper::ApplyMask(TextureId textureId, - Devel::PixelBuffer pixelBuffer, - Devel::PixelBuffer maskPixelBuffer, - float contentScale, - bool cropToMask, - DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad) -{ - mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId)); - auto id = GetImplementation(mLoader).ApplyMask(pixelBuffer, maskPixelBuffer, contentScale, cropToMask, preMultiplyOnLoad); - mLoadingInfoContainer.back().loadId = id; -} - -TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(AsyncLoadingHelper&& rhs) -: AsyncLoadingHelper(rhs.mLoader, rhs.mTextureManager, std::move(rhs.mLoadingInfoContainer)) -{ -} - -TextureManager::AsyncLoadingHelper::AsyncLoadingHelper( - Toolkit::AsyncImageLoader loader, - TextureManager& textureManager, - AsyncLoadingInfoContainerType&& loadingInfoContainer) -: mLoader(loader), - mTextureManager(textureManager), - mLoadingInfoContainer(std::move(loadingInfoContainer)) -{ - DevelAsyncImageLoader::PixelBufferLoadedSignal(mLoader).Connect( - this, &AsyncLoadingHelper::AsyncLoadComplete); -} - -void TextureManager::AsyncLoadingHelper::AsyncLoadComplete(uint32_t id, - Devel::PixelBuffer pixelBuffer) -{ - mTextureManager.AsyncLoadComplete(mLoadingInfoContainer, id, pixelBuffer); -} - -Geometry TextureManager::GetRenderGeometry(TextureId textureId, uint32_t& frontElements, uint32_t& backElements) -{ - return RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().GetGeometry(textureId, frontElements, backElements) : Geometry(); -} - -} // namespace Internal - -} // namespace Toolkit - -} // namespace Dali diff --git a/dali-toolkit/internal/visuals/texture-manager-impl.h b/dali-toolkit/internal/visuals/texture-manager-impl.h deleted file mode 100644 index 7ca7a05..0000000 --- a/dali-toolkit/internal/visuals/texture-manager-impl.h +++ /dev/null @@ -1,957 +0,0 @@ -#ifndef DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H -#define DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H - -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// EXTERNAL INCLUDES -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// INTERNAL INCLUDES -#include -#include -#include -#include -#include -#include -#include - -namespace Dali -{ -namespace Toolkit -{ -namespace Internal -{ -class ImageAtlasManager; -typedef IntrusivePtr ImageAtlasManagerPtr; - -/** - * The TextureManager provides a common Image loading API for Visuals. - * - * The TextureManager is responsible for providing sync, async, atlased and non-atlased loads. - * Texture caching is provided and performed when possible. - * Broken Images are automatically provided on load failure. - */ -class TextureManager : public ConnectionTracker -{ -public: - typedef int32_t TextureId; ///< The TextureId type. This is used as a handle to refer to a particular Texture. - static const int INVALID_TEXTURE_ID = -1; ///< Used to represent a null TextureId or error - - /** - * Whether the texture should be atlased or uploaded into it's own GPU texture - */ - enum UseAtlas - { - NO_ATLAS, - USE_ATLAS - }; - - /** - * Whether the pixel data should be kept in TextureManager, returned with pixelBuffer or uploaded for rendering - */ - enum class StorageType : uint8_t - { - 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. - }; - - /** - * Whether the texture should be loaded synchronously or asynchronously. - */ - enum class LoadType : uint8_t - { - LOAD_ASYNCHRONOUSLY, - LOAD_SYNCHRONOUSLY - }; - - /** - * @brief The LoadState Enumeration represents the current state of a particular Texture's life-cycle. - */ - enum class LoadState : uint8_t - { - NOT_STARTED, ///< Default - LOADING, ///< Loading has been started, but not finished. - LOAD_FINISHED, ///< Loading has finished. (for CPU storage only) - WAITING_FOR_MASK, ///< Loading has finished, but waiting for mask image - MASK_APPLYING, ///< Loading has finished, Mask is applying - MASK_APPLIED, ///< Loading has finished, Mask is applyied by GPU - UPLOADED, ///< Uploaded and ready. (For GPU upload only) - CANCELLED, ///< Removed before loading completed - LOAD_FAILED ///< Async loading failed, e.g. connection problem - }; - - /** - * @brief Types of reloading policies - */ - enum class ReloadPolicy - { - CACHED = 0, ///< Loads cached texture if it exists. - FORCED ///< Forces reloading of texture. - }; - - /** - * @brief Whether to multiply alpha into color channels on load - */ - enum class MultiplyOnLoad - { - LOAD_WITHOUT_MULTIPLY = 0, ///< Don't modify the image - MULTIPLY_ON_LOAD ///< Multiply alpha into color channels on load - }; - -public: - struct MaskingData - { - MaskingData(); - ~MaskingData() = default; - - VisualUrl mAlphaMaskUrl; - TextureManager::TextureId mAlphaMaskId; - float mContentScaleFactor; - bool mCropToMask; - }; - using MaskingDataPointer = std::unique_ptr; - - /** - * Class to provide lifecycle event on destruction of texture manager. - */ - struct LifecycleObserver - { - /** - * Called shortly before the texture manager is destroyed. - */ - virtual void TextureManagerDestroyed() = 0; - }; - - /** - * Constructor. - */ - TextureManager(); - - /** - * Destructor. - */ - ~TextureManager() override; - - // TextureManager Main API: - - /** - * @brief Requests an frame of animated image load. - * - * The parameters are used to specify how the animated image is loaded. - * The observer has the LoadComplete method called when the load is ready. - * - * @param[in] animatedImageLoading The AnimatedImageLoading that contain the animated image information - * @param[in] frameIndex The frame index to load. - * @param[in] samplingMode The SamplingMode to use - * @param[in] synchronousLoading true if the frame should be loaded synchronously - * @param[out] textureId The textureId of the frame - * @param[in] wrapModeU Horizontal Wrap mode - * @param[in] wrapModeV Vertical Wrap mode - * @param[in] textureObserver The client object should inherit from this and provide the "UploadCompleted" virtual. - * This is called when an image load completes (or fails). - * - * @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, - bool synchronousLoading, - TextureManager::TextureId& textureId, - Dali::WrapMode::Type wrapModeU, - Dali::WrapMode::Type wrapModeV, - TextureUploadObserver* textureObserver); - - /** - * @brief Requests an image load of the given URL to get PixelBuffer. - * - * The parameters are used to specify how the image is loaded. - * The observer has the LoadComplete method called when the load is ready. - * - * @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] synchronousLoading true if the URL should be loaded synchronously - * @param[in] textureObserver The client object should inherit from this and provide the "UploadCompleted" virtual. - * This is called when an image load completes (or fails). - * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data - * @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 - * - * @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, - Dali::SamplingMode::Type samplingMode, - bool synchronousLoading, - TextureUploadObserver* textureObserver, - bool orientationCorrection, - TextureManager::MultiplyOnLoad& preMultiplyOnLoad); - - /** - * @brief Requests an image load of the given URL. - * - * The parameters are used to specify how the image is loaded. - * The observer has the LoadComplete method called when the load is ready. - * - * When the client has finished with the Texture, Remove() should be called. - * - * @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, out] maskInfo Mask info structure - * @param[in] synchronousLoading true if the URL should be loaded synchronously - * @param[out] textureId, The textureId of the URL - * @param[out] textureRect The rectangle within the texture atlas that this URL occupies, - * this is the rectangle in normalized coordinates. - * @param[out] textureRectSize The rectangle within the texture atlas that this URL occupies, - * this is the same rectangle in pixels. - * @param[in,out] atlasingStatus Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still - * be loaded, and marked successful, but this will be set to false. - * If atlasing succeeds, this will be set to true. - * @param[out] loadingStatus The loading status of the texture - * @param[in] wrapModeU Horizontal Wrap mode - * @param[in] wrapModeV Vertical Wrap mode - * @param[in] textureObserver The client object should inherit from this and provide the "UploadCompleted" virtual. - * This is called when an image load completes (or fails). - * @param[in] atlasObserver This is used if the texture is atlased, and will be called instead of - * textureObserver.UploadCompleted - * @param[in] imageAtlasManager The atlas manager to use for atlasing textures - * @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 - * - * @return The texture set containing the image, or empty if still loading. - */ - TextureSet LoadTexture(const VisualUrl& url, - Dali::ImageDimensions desiredSize, - Dali::FittingMode::Type fittingMode, - Dali::SamplingMode::Type samplingMode, - MaskingDataPointer& maskInfo, - bool synchronousLoading, - TextureManager::TextureId& textureId, - Vector4& textureRect, - Dali::ImageDimensions& textureRectSize, - bool& atlasingStatus, - bool& loadingStatus, - Dali::WrapMode::Type wrapModeU, - Dali::WrapMode::Type wrapModeV, - TextureUploadObserver* textureObserver, - AtlasUploadObserver* atlasObserver, - ImageAtlasManagerPtr imageAtlasManager, - bool orientationCorrection, - TextureManager::ReloadPolicy reloadPolicy, - MultiplyOnLoad& preMultiplyOnLoad); - - /** - * @brief Requests an image load of the given URL. - * - * The parameters are used to specify how the image is loaded. - * The observer has the LoadComplete method called when the load is ready. - * - * When the client has finished with the Texture, Remove() should be called. - * - * @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] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful, - * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver. - * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual. - * This is called when an image load completes (or fails). - * @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, - const ImageDimensions desiredSize, - FittingMode::Type fittingMode, - Dali::SamplingMode::Type samplingMode, - const UseAtlas useAtlasing, - TextureUploadObserver* observer, - bool orientationCorrection, - TextureManager::ReloadPolicy reloadPolicy, - MultiplyOnLoad& preMultiplyOnLoad, - bool synchronousLoading = false); - - /** - * @brief Requests an image load of the given URL, when the texture has - * have loaded, it will perform a blend with the image mask, and upload - * the blended texture. - * - * The parameters are used to specify how the image is loaded. - * The observer has the LoadComplete method called when the load is ready. - * - * When the client has finished with the Texture, Remove() should be called. - * - * @param[in] url The URL of the image to load - * @param[in] maskTextureId The texture id of an image to mask this with - * (can be INVALID if no masking required) - * @param[in] contentScale The scale factor to apply to the image before masking - * @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] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still - * be loaded, and marked successful, - * but "useAtlasing" will be set to false in the "UploadCompleted" callback from - * the TextureManagerUploadObserver. - * @param[in] cropToMask Only used with masking, this will crop the scaled image to the mask size. - * If false, then the mask will be scaled to fit the image before being applied. - * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" - * virtual. - * This is called when an image load completes (or fails). - * @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] 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, - TextureId maskTextureId, - float contentScale, - const ImageDimensions desiredSize, - FittingMode::Type fittingMode, - Dali::SamplingMode::Type samplingMode, - const UseAtlas useAtlasing, - bool cropToMask, - TextureUploadObserver* observer, - bool orientationCorrection, - TextureManager::ReloadPolicy reloadPolicy, - 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, - bool synchronousLoading = false); - - /** - * @brief Remove a Texture from the TextureManager. - * - * Textures are cached and therefore only the removal of the last - * occurrence of a Texture will cause its removal internally. - * - * @param[in] textureId The ID of the Texture to remove. - * @param[in] textureObserver The texture observer. - */ - void Remove(const TextureManager::TextureId textureId, TextureUploadObserver* textureObserver); - - /** - * @brief Get the visualUrl associated with the texture id. - * @param[in] textureId The texture Id to get - * @return The visual Url associated with the texture id. - */ - VisualUrl GetVisualUrl(TextureId textureId); - - /** - * @brief Get the current state of a texture - * @param[in] textureId The texture id to query - * @return The loading state if the texture is valid, or NOT_STARTED if the textureId - * is not valid. - */ - LoadState GetTextureState(TextureId textureId); - - /** - * @brief Get the associated texture set if the texture id is valid - * @param[in] textureId The texture Id to look up - * @return the associated texture set, or an empty handle if textureId is not valid - */ - TextureSet GetTextureSet(TextureId textureId); - - /** - * @brief Get the encoded image buffer - * @param[in] textureId The textureId to look up - * @return the encoded image buffer, or an empty handle if textureId is not valid - */ - EncodedImageBuffer GetEncodedImageBuffer(TextureId textureId); - - /** - * @brief Get the encoded image buffer by VisualUrl - * @param[in] url The url to look up - * @return the encoded image buffer, or an empty handle if url is not buffer resource or buffer is not valid - */ - EncodedImageBuffer GetEncodedImageBuffer(const std::string& url); - - /** - * Adds an external texture to the texture manager - * @param[in] texture The texture to add - * @return string containing the URL for the texture - */ - std::string AddExternalTexture(TextureSet& texture); - - /** - * Adds an external encoded image buffer to the texture manager - * @param[in] encodedImageBuffer The image buffer to add - * @return string containing the URL for the texture - */ - std::string AddExternalEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer); - - /** - * Removes an external texture from texture manager - * @param[in] url The string containing the texture to remove - * @return handle to the texture - */ - TextureSet RemoveExternalTexture(const std::string& url); - - /** - * Removes an external encoded image buffer from texture manager - * @param[in] url The string containing the encoded image buffer to remove - * @return handle to the encoded image buffer - */ - EncodedImageBuffer RemoveExternalEncodedImageBuffer(const std::string& url); - - /** - * @brief Notify that external textures or external encoded image buffers are used. - * @param[in] url The URL of the texture to use. - */ - void UseExternalResource(const VisualUrl& url); - - /** - * Add an observer to the object. - * @param[in] observer The observer to add. - */ - void AddObserver(TextureManager::LifecycleObserver& observer); - - /** - * Remove an observer from the object - * @pre The observer has already been added. - * @param[in] observer The observer to remove. - */ - void RemoveObserver(TextureManager::LifecycleObserver& observer); - - /** - * @brief Returns the geometry associated with texture. - * @param[in] textureId Id of the texture - * @param[out] frontElements number of front elements - * @param[out] backElements number of back elements - * @return Returns valid geometry object - */ - Geometry GetRenderGeometry(TextureId textureId, uint32_t& frontElements, uint32_t& backElements); - -private: - /** - * @brief Requests an image load of the given URL, when the texture has - * have loaded, if there is a valid maskTextureId, it will perform a - * CPU blend with the mask, and upload the blend texture. - * - * The parameters are used to specify how the image is loaded. - * The observer has the LoadComplete method called when the load is ready. - * - * When the client has finished with the Texture, Remove() should be called. - * - * @param[in] url The URL of the image to load - * @param[in] maskTextureId The texture id of an image to use as a mask. If no mask is required, then set - * to INVALID_TEXTURE_ID - * @param[in] contentScale The scaling factor to apply to the content when masking - * @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] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be - * loaded, and marked successful, but "useAtlasing" will be set to false in the - * "UploadCompleted" callback from the TextureManagerUploadObserver. - * @param[in] cropToMask Whether to crop the target after masking, or scale the mask to the image before - * masking. - * @param[in] storageType, Whether the pixel data is stored in the cache or uploaded to the GPU - * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" - * virtual. - * This is called when an image load completes (or fails). - * @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] preMultiplyOnLoad True if the image color should be multiplied by it's alpha. Set to false if - * 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( - const VisualUrl& url, - TextureId maskTextureId, - float contentScale, - const ImageDimensions desiredSize, - FittingMode::Type fittingMode, - Dali::SamplingMode::Type samplingMode, - UseAtlas useAtlas, - bool cropToMask, - StorageType storageType, - TextureUploadObserver* observer, - bool orientationCorrection, - TextureManager::ReloadPolicy reloadPolicy, - MultiplyOnLoad& preMultiplyOnLoad, - Dali::AnimatedImageLoading animatedImageLoading, - uint32_t frameIndex, - bool synchronousLoading); - - /** - * @brief Get the current state of a texture - * @param[in] textureId The texture id to query - * @return The loading state if the texture is valid, or NOT_STARTED if the textureId - * is not valid. - */ - 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: - - /** - * @brief This struct is used to manage the life-cycle of Texture loading and caching. - */ - struct TextureInfo - { - TextureInfo(TextureId textureId, - TextureId maskTextureId, - const VisualUrl& url, - ImageDimensions desiredSize, - float scaleFactor, - FittingMode::Type fittingMode, - Dali::SamplingMode::Type samplingMode, - bool loadSynchronously, - bool cropToMask, - UseAtlas useAtlas, - TextureManager::TextureHash hash, - bool orientationCorrection, - bool preMultiplyOnLoad, - Dali::AnimatedImageLoading animatedImageLoading, - uint32_t frameIndex) - : url(url), - desiredSize(desiredSize), - useSize(desiredSize), - atlasRect(0.0f, 0.0f, 1.0f, 1.0f), // Full atlas rectangle - textureId(textureId), - maskTextureId(maskTextureId), - hash(hash), - scaleFactor(scaleFactor), - referenceCount(1u), - loadState(LoadState::NOT_STARTED), - fittingMode(fittingMode), - samplingMode(samplingMode), - storageType(StorageType::UPLOAD_TO_TEXTURE), - animatedImageLoading(animatedImageLoading), - frameIndex(frameIndex), - loadSynchronously(loadSynchronously), - useAtlas(useAtlas), - cropToMask(cropToMask), - orientationCorrection(true), - preMultiplyOnLoad(preMultiplyOnLoad), - preMultiplied(false) - { - } - - /** - * Container type used to store all observer clients of this Texture - */ - typedef Dali::Vector ObserverListType; - - ObserverListType observerList; ///< Container used to store all observer clients of this Texture - Toolkit::ImageAtlas atlas; ///< The atlas this Texture lays within (if any) - Devel::PixelBuffer pixelBuffer; ///< The PixelBuffer holding the image data (May be empty after upload) - TextureSet textureSet; ///< The TextureSet holding the Texture - VisualUrl url; ///< The URL of the image - ImageDimensions desiredSize; ///< The size requested - ImageDimensions useSize; ///< The size used - Vector4 atlasRect; ///< The atlas rect used if atlased - TextureId textureId; ///< The TextureId associated with this Texture - TextureId maskTextureId; ///< The mask TextureId to be applied on load - TextureManager::TextureHash hash; ///< The hash used to cache this Texture - float scaleFactor; ///< The scale factor to apply to the Texture when masking - int16_t referenceCount; ///< The reference count of clients using this Texture - LoadState loadState; ///< The load state showing the load progress of the Texture - FittingMode::Type fittingMode : 3; ///< The requested FittingMode - Dali::SamplingMode::Type samplingMode : 3; ///< The requested SamplingMode - StorageType storageType; ///< CPU storage / GPU upload; - Dali::AnimatedImageLoading animatedImageLoading; ///< AnimatedImageLoading that contains animated image information. - uint32_t frameIndex; ///< frame index that be loaded, in case of animated image - bool loadSynchronously : 1; ///< True if synchronous loading was requested - UseAtlas useAtlas : 2; ///< USE_ATLAS if an atlas was requested. - ///< This is updated to false if atlas is not used - bool cropToMask : 1; ///< true if the image should be cropped to the mask size. - bool orientationCorrection : 1; ///< true if the image should be rotated to match exif orientation data - bool preMultiplyOnLoad : 1; ///< true if the image's color should be multiplied by it's alpha - bool preMultiplied : 1; ///< true if the image's color was multiplied by it's alpha - }; - - /** - * Structure to hold info about a texture load queued during NotifyObservers - */ - struct LoadQueueElement - { - LoadQueueElement(TextureId textureId, TextureUploadObserver* observer) - : mTextureId(textureId), - mObserver(observer) - { - } - - TextureId mTextureId; ///< The texture id of the requested load. - TextureUploadObserver* mObserver; ///< Observer of texture load. - }; - - /** - * Struct to hold information about a requested Async load. - * This is used to look up a TextureManager::TextureId from the returned AsyncLoad Id. - */ - struct AsyncLoadingInfo - { - AsyncLoadingInfo(TextureId textureId) - : textureId(textureId), - loadId(0) - { - } - - TextureId textureId; ///< The external Texture Id assigned to this load - uint32_t loadId; ///< The load Id used by the async loader to reference this load - }; - - // Private typedefs: - - typedef std::deque AsyncLoadingInfoContainerType; ///< The container type used to manage Asynchronous loads in progress - typedef std::vector TextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures - - /** - * @brief Initiate a load or queue load if NotifyObservers is invoking callbacks - * @param[in] textureInfo The TextureInfo struct associated with the Texture - * @param[in] observer The observer wishing to observe the texture upload - */ - void LoadOrQueueTexture(TextureInfo& textureInfo, TextureUploadObserver* observer); - - /** - * @brief Queue a texture load to be subsequently handled by ProcessQueuedTextures. - * @param[in] textureInfo The TextureInfo struct associated with the Texture - * @param[in] observer The observer wishing to observe the texture upload - */ - void QueueLoadTexture(TextureInfo& textureInfo, TextureUploadObserver* observer); - - /** - * @brief Used internally to initiate a load. - * @param[in] textureInfo The TextureInfo struct associated with the Texture - * @param[in] observer The observer wishing to observe the texture upload - */ - void LoadTexture(TextureInfo& textureInfo, TextureUploadObserver* observer); - - /** - * @brief Initiate load of textures queued whilst NotifyObservers invoking callbacks. - */ - void ProcessQueuedTextures(); - - /** - * Add the observer to the observer list - * @param[in] textureInfo The TextureInfo struct associated with the texture - * @param[in] observer The observer wishing to observe the texture upload - */ - void ObserveTexture(TextureInfo& textureInfo, TextureUploadObserver* observer); - - /** - * @brief This signal handler is called when the async local loader finishes loading. - * @param[in] id This is the async image loaders Id - * @param[in] pixelBuffer The loaded image data - */ - void AsyncLocalLoadComplete(uint32_t id, Devel::PixelBuffer pixelBuffer); - - /** - * @brief This signal handler is called when the async local loader finishes loading. - * @param[in] id This is the async image loaders Id - * @param[in] pixelBuffer The loaded image data - */ - void AsyncRemoteLoadComplete(uint32_t id, Devel::PixelBuffer pixelBuffer); - - /** - * Common method to handle loading completion - * @param[in] container The Async loading container - * @param[in] id This is the async image loaders Id - * @param[in] pixelBuffer The loaded image data - */ - void AsyncLoadComplete(AsyncLoadingInfoContainerType& container, uint32_t id, Devel::PixelBuffer pixelBuffer); - - /** - * @brief Performs Post-Load steps including atlasing. - * @param[in] textureInfo The struct associated with this Texture - * @param[in] pixelBuffer The image pixelBuffer - * @return True if successful - */ - void PostLoad(TextureManager::TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer); - - /** - * Check if there is a texture waiting to be masked. If there - * is then apply this mask and upload it. - * @param[in] maskTextureInfo The texture info of the mask that has just loaded. - */ - void CheckForWaitingTexture(TextureInfo& maskTextureInfo); - - /** - * Apply the mask to the pixelBuffer. - * @param[in] textureInfo The information of texture to apply the mask to - * @param[in] maskTextureId The texture id of the mask. - */ - void ApplyMask(TextureInfo& textureInfo, TextureId maskTextureId); - - /** - * Upload the texture specified in pixelBuffer to the appropriate location - * @param[in] pixelBuffer The image data to upload - * @param[in] textureInfo The texture info containing the location to - * store the data to. - */ - void UploadTexture(Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo); - - /** - * Creates tiled geometry of for the texture which separates fully-opaque - * tiles from ones which use transparency. - * @param pixelBuffer - * @param textureInfo - */ - bool CreateTiledGeometry(const Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo); - - /** - * Notify the current observers that the texture upload is complete, - * then remove the observers from the list. - * @param[in] textureInfo The struct associated with this Texture - * @param[in] success If the pixel data was retrieved successfully and uploaded to GPU - */ - void NotifyObservers(TextureInfo& textureInfo, bool success); - - /** - * @brief Generates a new, unique TextureId - * @return A unique TextureId - */ - TextureManager::TextureId GenerateUniqueTextureId(); - - /** - * @brief Used to lookup an index into the TextureInfoContainer from a TextureId - * @param[in] textureId The TextureId to look up - * @return The cache index - */ - int GetCacheIndexFromId(TextureId textureId); - - /** - * @brief Generates a hash for caching based on the input parameters. - * Only applies size, fitting mode andsampling mode if the size is specified. - * Only applies maskTextureId if it isn't INVALID_TEXTURE_ID - * Always applies useAtlas. - * @param[in] url The URL of the image to load - * @param[in] size The image size - * @param[in] fittingMode The FittingMode to use - * @param[in] samplingMode The SamplingMode to use - * @param[in] useAtlas True if atlased - * @param[in] maskTextureId The masking texture id (or INVALID_TEXTURE_ID) - * @return A hash of the provided data for caching. - */ - TextureHash GenerateHash(const std::string& url, const ImageDimensions size, const FittingMode::Type fittingMode, const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas, TextureId maskTextureId); - - /** - * @brief Looks up a cached texture by its hash. - * If found, the given parameters are used to check there is no hash-collision. - * @param[in] hash The hash to look up - * @param[in] url The URL of the image to load - * @param[in] size The image size - * @param[in] fittingMode The FittingMode to use - * @param[in] samplingMode The SamplingMode to use - * @param[in] useAtlas True if atlased - * @param[in] maskTextureId Optional texture ID to use to mask this image - * @param[in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha. - * @return A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found. - */ - TextureManager::TextureId FindCachedTexture( - const TextureManager::TextureHash hash, - const std::string& url, - const ImageDimensions size, - const FittingMode::Type fittingMode, - const Dali::SamplingMode::Type samplingMode, - const bool useAtlas, - TextureId maskTextureId, - MultiplyOnLoad preMultiplyOnLoad); - -private: - /** - * @brief Helper class to keep the relation between AsyncImageLoader and corresponding LoadingInfo container - */ - class AsyncLoadingHelper : public ConnectionTracker - { - public: - /** - * @brief Create an AsyncLoadingHelper. - * @param[in] textureManager Reference to the texture manager - */ - AsyncLoadingHelper(TextureManager& textureManager); - - /** - * @brief Load a new frame of animated image - * @param[in] textureId TextureId to reference the texture that will be loaded - * @param[in] animatedImageLoading The AnimatedImageLoading to load animated image - * @param[in] frameIndex The frame index of a frame to be loaded frame - */ - void LoadAnimatedImage(TextureId textureId, - Dali::AnimatedImageLoading animatedImageLoading, - uint32_t frameIndex); - - /** - * @brief Load a new texture. - * @param[in] textureId TextureId to reference the texture that will be loaded - * @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 - * @param[in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha or if the image need to be applied alpha mask. - */ - void Load(TextureId textureId, - const VisualUrl& url, - ImageDimensions desiredSize, - FittingMode::Type fittingMode, - SamplingMode::Type samplingMode, - bool orientationCorrection, - DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad); - - /** - * @brief Apply mask - * @param [in] id of the texture - * @param [in] pixelBuffer of the to be masked image - * @param [in] maskPixelBuffer of the mask image - * @param [in] contentScale The factor to scale the content - * @param [in] cropToMask Whether to crop the content to the mask size - * @param [in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha. - */ - void ApplyMask(TextureId textureId, - Devel::PixelBuffer pixelBuffer, - Devel::PixelBuffer maskPixelBuffer, - float contentScale, - bool cropToMask, - DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad); - - public: - AsyncLoadingHelper(const AsyncLoadingHelper&) = delete; - AsyncLoadingHelper& operator=(const AsyncLoadingHelper&) = delete; - - AsyncLoadingHelper(AsyncLoadingHelper&& rhs); - AsyncLoadingHelper& operator=(AsyncLoadingHelper&& rhs) = delete; - - private: - /** - * @brief Main constructor that used by all other constructors - */ - AsyncLoadingHelper(Toolkit::AsyncImageLoader loader, - TextureManager& textureManager, - AsyncLoadingInfoContainerType&& loadingInfoContainer); - - /** - * @brief Callback to be called when texture loading is complete, it passes the pixel buffer on to texture manager. - * @param[in] id Loader id - * @param[in] pixelBuffer Image data - */ - void AsyncLoadComplete(uint32_t id, Devel::PixelBuffer pixelBuffer); - - private: - Toolkit::AsyncImageLoader mLoader; - TextureManager& mTextureManager; - AsyncLoadingInfoContainerType mLoadingInfoContainer; - }; - - struct ExternalTextureInfo - { - TextureId textureId; - TextureSet textureSet; - int16_t referenceCount{1}; - }; - - struct EncodedBufferTextureInfo - { - EncodedBufferTextureInfo(TextureId textureId, - const EncodedImageBuffer& encodedImageBuffer) - : textureId(textureId), - encodedImageBuffer(encodedImageBuffer), - referenceCount(1u) - { - } - TextureId textureId; - EncodedImageBuffer encodedImageBuffer; - int16_t referenceCount; - }; - -private: - /** - * Deleted copy constructor. - */ - TextureManager(const TextureManager&) = delete; - - /** - * Deleted assignment operator. - */ - TextureManager& operator=(const TextureManager& rhs) = delete; - - /** - * This is called by the TextureManagerUploadObserver when an observer is destroyed. - * We use the callback to know when to remove an observer from our notify list. - * @param[in] observer The observer that generated the callback - */ - void ObserverDestroyed(TextureUploadObserver* observer); - -private: // Member Variables: - TextureInfoContainerType mTextureInfoContainer; ///< Used to manage the life-cycle and caching of Textures - RoundRobinContainerView mAsyncLocalLoaders; ///< The Asynchronous image loaders used to provide all local async loads - RoundRobinContainerView mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads - std::vector mExternalTextures; ///< Externally provided textures - std::vector mEncodedBufferTextures; ///< Externally encoded buffer textures - Dali::Vector mLifecycleObservers; ///< Lifecycle observers of texture manager - Dali::Vector mLoadQueue; ///< Queue of textures to load after NotifyObservers - TextureId mCurrentTextureId; ///< The current value used for the unique Texture Id generation - bool mQueueLoadFlag; ///< Flag that causes Load Textures to be queued. -}; - -} // namespace Internal - -} // namespace Toolkit - -} // namespace Dali - -#endif // DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H diff --git a/dali-toolkit/internal/visuals/visual-factory-cache.h b/dali-toolkit/internal/visuals/visual-factory-cache.h index b19bad4..3ed626f 100644 --- a/dali-toolkit/internal/visuals/visual-factory-cache.h +++ b/dali-toolkit/internal/visuals/visual-factory-cache.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_VISUAL_FACTORY_CACHE_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,9 +25,9 @@ #include // INTERNAL INCLUDES +#include #include #include -#include #include namespace Dali @@ -298,12 +298,12 @@ private: struct BrokenImageInfo { BrokenImageInfo() - :visualType(), - url(""), - npatchId(NPatchData::INVALID_NPATCH_DATA_ID), - texture(), - width(0), - height(0) + : visualType(), + url(""), + npatchId(NPatchData::INVALID_NPATCH_DATA_ID), + texture(), + width(0), + height(0) { } @@ -312,20 +312,20 @@ private: } // Data - VisualUrl::Type visualType; - std::string url; - NPatchData::NPatchDataId npatchId; - Texture texture; - uint32_t width; - uint32_t height; + VisualUrl::Type visualType; + std::string url; + NPatchData::NPatchDataId npatchId; + Texture texture; + uint32_t width; + uint32_t height; }; Geometry mGeometry[GEOMETRY_TYPE_MAX + 1]; Shader mShader[SHADER_TYPE_MAX + 1]; - ImageAtlasManagerPtr mAtlasManager; - TextureManager mTextureManager; - NPatchLoader mNPatchLoader; + ImageAtlasManagerPtr mAtlasManager; + TextureManager mTextureManager; + NPatchLoader mNPatchLoader; SvgRasterizeThread* mSvgRasterizeThread; std::unique_ptr mVectorAnimationManager; -- 2.7.4