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 <eunkiki.hong@samsung.com>
#include <dali-toolkit-test-suite-utils.h>
#include <toolkit-timer.h>
#include <toolkit-event-thread-callback.h>
-#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
-#include <dali-toolkit/internal/visuals/texture-upload-observer.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-upload-observer.h>
#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
#include <dali-toolkit/internal/visuals/visual-factory-impl.h> ///< For VisualFactory's member TextureManager.
ImageDimensions(),
FittingMode::SCALE_TO_FILL,
SamplingMode::BOX_THEN_LINEAR,
- TextureManager::NO_ATLAS,
+ TextureManager::UseAtlas::NO_ATLAS,
&observer,
true,
TextureManager::ReloadPolicy::CACHED,
ImageDimensions(),
FittingMode::SCALE_TO_FILL,
SamplingMode::BOX_THEN_LINEAR,
- TextureManager::USE_ATLAS,
+ TextureManager::UseAtlas::NO_ATLAS,
&observer,
true,
TextureManager::ReloadPolicy::CACHED,
ImageDimensions(),
FittingMode::SCALE_TO_FILL,
SamplingMode::BOX_THEN_LINEAR,
- TextureManager::NO_ATLAS,
+ TextureManager::UseAtlas::NO_ATLAS,
&observer1,
true, ///< orientationCorrection
TextureManager::ReloadPolicy::CACHED,
ImageDimensions(),
FittingMode::SCALE_TO_FILL,
SamplingMode::BOX_THEN_LINEAR,
- TextureManager::NO_ATLAS,
+ TextureManager::UseAtlas::NO_ATLAS,
&observer3,
true, ///< orientationCorrection
TextureManager::ReloadPolicy::CACHED,
ImageDimensions(),
FittingMode::SCALE_TO_FILL,
SamplingMode::BOX_THEN_LINEAR,
- TextureManager::NO_ATLAS,
+ TextureManager::UseAtlas::NO_ATLAS,
&observer4,
true, ///< orientationCorrection
TextureManager::ReloadPolicy::FORCED,
ImageDimensions(),
FittingMode::SCALE_TO_FILL,
SamplingMode::BOX_THEN_LINEAR,
- TextureManager::NO_ATLAS,
+ TextureManager::UseAtlas::NO_ATLAS,
&observer1,
true, ///< orientationCorrection
TextureManager::ReloadPolicy::CACHED,
ImageDimensions(),
FittingMode::SCALE_TO_FILL,
SamplingMode::BOX_THEN_LINEAR,
- TextureManager::NO_ATLAS,
+ TextureManager::UseAtlas::NO_ATLAS,
&observer1,
true,
TextureManager::ReloadPolicy::CACHED,
${toolkit_src_dir}/visuals/svg/svg-rasterize-thread.cpp
${toolkit_src_dir}/visuals/svg/svg-visual.cpp
${toolkit_src_dir}/visuals/text/text-visual.cpp
+ ${toolkit_src_dir}/visuals/texture-manager/texture-async-loading-helper.cpp
+ ${toolkit_src_dir}/visuals/texture-manager/texture-cache-manager.cpp
+ ${toolkit_src_dir}/visuals/texture-manager/texture-manager-impl.cpp
+ ${toolkit_src_dir}/visuals/texture-manager/texture-upload-observer.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
// INTERNAL INCLUDES
#include <dali-toolkit/devel-api/image-loader/texture-manager.h>
#include <dali-toolkit/internal/visuals/visual-factory-impl.h>
-#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-impl.h>
#include <dali-toolkit/internal/visuals/visual-url.h>
namespace Dali
// EXTERNAL INCLUDES
#include <dali-toolkit/internal/visuals/animated-image/image-cache.h>
-#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-impl.h>
namespace Dali
{
*/
// EXTERNAL INCLUDES
-#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
-#include <dali-toolkit/internal/visuals/texture-upload-observer.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-upload-observer.h>
namespace Dali
{
// EXTERNAL INCLUDES
#include <dali-toolkit/internal/visuals/animated-image/image-cache.h>
-#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-impl.h>
#include <dali/devel-api/adaptor-framework/animated-image-loading.h>
#include <dali/devel-api/common/circular-queue.h>
// EXTERNAL INCLUDES
#include <dali-toolkit/internal/visuals/animated-image/image-cache.h>
-#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-impl.h>
#include <dali/devel-api/common/circular-queue.h>
namespace Dali
#include <dali-toolkit/devel-api/visuals/image-visual-actions-devel.h>
#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
-#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-impl.h>
#include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
#include <dali-toolkit/internal/visuals/visual-base-impl.h>
#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
// INTERNAL INCLUDES
#include <dali-toolkit/devel-api/image-loader/atlas-upload-observer.h>
#include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
-#include <dali-toolkit/internal/visuals/texture-upload-observer.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-upload-observer.h>
#include <dali-toolkit/internal/visuals/visual-base-impl.h>
#include <dali-toolkit/internal/visuals/visual-url.h>
#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
// INTERNAL INCLUDES
#include <dali-toolkit/devel-api/utility/npatch-utilities.h>
-#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-impl.h>
#include <dali-toolkit/internal/visuals/visual-url.h>
namespace Dali
// INTERNAL INCLUDES
#include <dali-toolkit/devel-api/utility/npatch-utilities.h>
#include <dali-toolkit/internal/visuals/npatch-data.h>
-#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-impl.h>
#include <dali-toolkit/internal/visuals/visual-url.h>
namespace Dali
#include <dali/public-api/rendering/shader.h>
// INTERNAL INCLUDES
-#include <dali-toolkit/internal/visuals/texture-upload-observer.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-upload-observer.h>
#include <dali-toolkit/internal/visuals/visual-base-impl.h>
#include <dali-toolkit/internal/visuals/visual-url.h>
#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
+++ /dev/null
-/*
- * 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 <dali-toolkit/internal/visuals/texture-manager-impl.h>
-
-// EXTERNAL HEADERS
-#include <dali/devel-api/adaptor-framework/environment-variable.h>
-#include <dali/devel-api/adaptor-framework/image-loading.h>
-#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
-#include <dali/devel-api/common/hash.h>
-#include <dali/integration-api/debug.h>
-#include <dali/public-api/math/vector4.h>
-#include <dali/public-api/rendering/geometry.h>
-#include <cstdlib>
-#include <string>
-
-// INTERNAL HEADERS
-#include <dali-toolkit/internal/image-loader/async-image-loader-impl.h>
-#include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
-#include <dali-toolkit/internal/visuals/rendering-addon.h>
-#include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
-
-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<unsigned char*>(&(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
+++ /dev/null
-#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 <dali/devel-api/adaptor-framework/pixel-buffer.h>
-#include <dali/devel-api/common/owner-container.h>
-#include <dali/public-api/adaptor-framework/encoded-image-buffer.h>
-#include <dali/public-api/common/dali-vector.h>
-#include <dali/public-api/object/ref-object.h>
-#include <dali/public-api/rendering/geometry.h>
-#include <dali/public-api/rendering/texture-set.h>
-#include <deque>
-#include <functional>
-#include <memory>
-#include <string>
-
-// INTERNAL INCLUDES
-#include <dali-toolkit/devel-api/image-loader/async-image-loader-devel.h>
-#include <dali-toolkit/devel-api/image-loader/image-atlas.h>
-#include <dali-toolkit/internal/helpers/round-robin-container-view.h>
-#include <dali-toolkit/internal/image-loader/async-image-loader-impl.h>
-#include <dali-toolkit/internal/visuals/texture-upload-observer.h>
-#include <dali-toolkit/internal/visuals/visual-url.h>
-#include <dali-toolkit/public-api/image-loader/async-image-loader.h>
-
-namespace Dali
-{
-namespace Toolkit
-{
-namespace Internal
-{
-class ImageAtlasManager;
-typedef IntrusivePtr<ImageAtlasManager> 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<MaskingData>;
-
- /**
- * 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<TextureUploadObserver*> 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<AsyncLoadingInfo> AsyncLoadingInfoContainerType; ///< The container type used to manage Asynchronous loads in progress
- typedef std::vector<TextureInfo> 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<AsyncLoadingHelper> mAsyncLocalLoaders; ///< The Asynchronous image loaders used to provide all local async loads
- RoundRobinContainerView<AsyncLoadingHelper> mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads
- std::vector<ExternalTextureInfo> mExternalTextures; ///< Externally provided textures
- std::vector<EncodedBufferTextureInfo> mEncodedBufferTextures; ///< Externally encoded buffer textures
- Dali::Vector<LifecycleObserver*> mLifecycleObservers; ///< Lifecycle observers of texture manager
- Dali::Vector<LoadQueueElement> 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
--- /dev/null
+/*
+ * 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 <dali/integration-api/debug.h>
+
+// INTERNAL HEADERS
+#include <dali-toolkit/internal/image-loader/async-image-loader-impl.h>
+#include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
+
+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
--- /dev/null
+#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 <dali/public-api/signals/connection-tracker.h>
+#include <deque>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-impl.h>
+#include <dali-toolkit/internal/image-loader/async-image-loader-impl.h>
+
+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<AsyncLoadingInfo> 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
--- /dev/null
+/*
+ * 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 <dali/devel-api/common/hash.h>
+#include <dali/integration-api/debug.h>
+
+// 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<TextureCacheIndex>(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<unsigned char*>(&(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<TextureCacheIndex>(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<TextureCacheIndex>(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
--- /dev/null
+#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 <dali/public-api/adaptor-framework/encoded-image-buffer.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-type.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-upload-observer.h>
+#include <dali-toolkit/internal/visuals/visual-url.h>
+
+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<TextureCacheManager::TextureInfo> TextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures
+ typedef std::vector<TextureCacheManager::ExternalTextureInfo> ExternalTextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures
+ typedef std::vector<TextureCacheManager::EncodedBufferTextureInfo> 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
--- /dev/null
+/*
+ * 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 <dali/devel-api/adaptor-framework/environment-variable.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/rendering/geometry.h>
+
+// INTERNAL HEADERS
+#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/rendering-addon.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-async-loading-helper.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-cache-manager.h>
+
+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 succes.
+ // So copy inpued 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 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)
+ {
+ 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<TextureCacheIndex>(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, 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<TextureCacheIndex>(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
--- /dev/null
+#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 <dali/devel-api/adaptor-framework/animated-image-loading.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/public-api/adaptor-framework/encoded-image-buffer.h>
+#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/rendering/geometry.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/image-loader/image-atlas.h>
+#include <dali-toolkit/internal/helpers/round-robin-container-view.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-cache-manager.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-type.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-upload-observer.h>
+#include <dali-toolkit/internal/visuals/visual-url.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+class ImageAtlasManager;
+typedef IntrusivePtr<ImageAtlasManager> 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<MaskingData>;
+
+ /**
+ * 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<TextureAsyncLoadingHelper> mAsyncLocalLoaders; ///< The Asynchronous image loaders used to provide all local async loads
+ RoundRobinContainerView<TextureAsyncLoadingHelper> mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads
+
+ Dali::Vector<LifecycleObserver*> mLifecycleObservers; ///< Lifecycle observers of texture manager
+ Dali::Vector<LoadQueueElement> 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
--- /dev/null
+#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 <dali/devel-api/adaptor-framework/animated-image-loading.h>
+#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/math/vector4.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/image-loader/image-atlas.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-upload-observer.h>
+#include <dali-toolkit/internal/visuals/visual-url.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+/**
+ * @brief Define common type, enum, and struct that texture-manager-impl and texture-cache-manager 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.
+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),
+ 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<TextureUploadObserver*> 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
+ bool loadSynchronously : 1; ///< True if synchronous loading was requested
+ UseAtlas useAtlas; ///< 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
+};
+
+} // namespace TextureManagerType
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXTURE_MANAGER_TYPE_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.
#include "texture-upload-observer.h"
// INTERNAL INCLUDES
-#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-type.h> // for INVALUD_TEXTURE_ID
namespace Dali
{
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),
#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.
// INTERNAL INCLUDES
#include <dali-toolkit/internal/visuals/npatch-loader.h>
#include <dali-toolkit/internal/visuals/svg/svg-rasterize-thread.h>
-#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/texture-manager/texture-manager-impl.h>
#include <dali/devel-api/rendering/renderer-devel.h>
namespace Dali