/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 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 <dali/devel-api/adaptor-framework/environment-variable.h>
#include <dali/devel-api/adaptor-framework/image-loading.h>
#include <dali/devel-api/common/hash.h>
-#include <dali/devel-api/images/texture-set-image.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/image-loader/image-atlas-impl.h>
#include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/rendering-addon.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};
#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
mAsyncRemoteLoaders( GetNumberOfRemoteLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ),
mExternalTextures(),
mLifecycleObservers(),
+ mLoadQueue(),
mBrokenImageUrl(""),
- mCurrentTextureId( 0 )
+ mCurrentTextureId( 0 ),
+ mQueueLoadFlag(false)
{
+ // Initialize the AddOn
+ RenderingAddOn::Get();
}
TextureManager::~TextureManager()
}
}
+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 )
+ {
+ // use broken image
+ pixelBuffer = LoadImageFromFile( mBrokenImageUrl );
+ PixelData pixelData;
+ if( pixelBuffer )
+ {
+ pixelData = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
+ }
+ 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
+ {
+ 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 );
+ TextureManager::LoadState loadState = GetTextureStateInternal( textureId );
+ if( loadState == TextureManager::LoadState::UPLOADED )
+ {
+ // UploadComplete 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() )
+ {
+ 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 );
+ }
+
+ return pixelBuffer;
+}
+
TextureSet TextureManager::LoadTexture(
const VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode,
- Dali::SamplingMode::Type samplingMode, const MaskingDataPointer& maskInfo,
+ 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,
{
Devel::PixelBuffer pixelBuffer = LoadImageFromFile( url.GetUrl(), desiredSize, fittingMode, samplingMode,
orientationCorrection );
+ if( maskInfo && maskInfo->mAlphaMaskUrl.IsValid() )
+ {
+ Devel::PixelBuffer maskPixelBuffer = LoadImageFromFile( maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(),
+ FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true );
+ if( maskPixelBuffer )
+ {
+ pixelBuffer.ApplyMask( maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask );
+ }
+ }
if( pixelBuffer )
{
PreMultiply( pixelBuffer, preMultiplyOnLoad );
if( !data )
{
// use broken image
- textureSet = TextureSet::New();
Devel::PixelBuffer pixelBuffer = LoadImageFromFile( mBrokenImageUrl );
if( pixelBuffer )
{
if( !textureSet ) // big image, no atlasing or atlasing failed
{
atlasingStatus = false;
- if( !maskInfo )
+ if( !maskInfo || !maskInfo->mAlphaMaskUrl.IsValid() )
{
textureId = RequestLoad( url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS,
textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad );
}
else
{
+ maskInfo->mAlphaMaskId = RequestMaskLoad( maskInfo->mAlphaMaskUrl );
textureId = RequestLoad( url,
maskInfo->mAlphaMaskId,
maskInfo->mContentScaleFactor,
}
TextureManager::LoadState loadState = GetTextureStateInternal( textureId );
- loadingStatus = ( loadState == TextureManager::LOADING );
-
- if( loadState == TextureManager::UPLOADED )
+ if( loadState == TextureManager::LoadState::UPLOADED )
{
// UploadComplete 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
{
TextureManager::MultiplyOnLoad& preMultiplyOnLoad )
{
return RequestLoadInternal( url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas,
- false, UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy,
- preMultiplyOnLoad );
+ false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy,
+ preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u );
}
TextureManager::TextureId TextureManager::RequestLoad(
TextureManager::MultiplyOnLoad& preMultiplyOnLoad )
{
return RequestLoadInternal( url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas,
- cropToMask, UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy,
- preMultiplyOnLoad );
+ cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy,
+ preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u );
}
TextureManager::TextureId TextureManager::RequestMaskLoad( const VisualUrl& maskUrl )
// 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, KEEP_PIXEL_BUFFER, NULL, true,
- TextureManager::ReloadPolicy::CACHED, preMultiply );
+ SamplingMode::NO_FILTER, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true,
+ TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u );
}
TextureManager::TextureId TextureManager::RequestLoadInternal(
TextureUploadObserver* observer,
bool orientationCorrection,
TextureManager::ReloadPolicy reloadPolicy,
- TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
+ TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
+ Dali::AnimatedImageLoading animatedImageLoading,
+ uint32_t frameIndex )
{
// First check if the requested Texture is cached.
- const TextureHash textureHash = GenerateHash( url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas,
- maskTextureId, preMultiplyOnLoad );
+ bool isAnimatedImage = ( animatedImageLoading ) ? true : false;
- TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
+ 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.
- int cacheIndex = FindCachedTexture( textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas,
- maskTextureId, preMultiplyOnLoad );
+ // 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 )
{
++( mTextureInfoContainer[ cacheIndex ].referenceCount );
}
textureId = mTextureInfoContainer[ cacheIndex ].textureId;
- DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d\n",
+
+ // 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 );
}
mTextureInfoContainer.push_back( TextureInfo( textureId, maskTextureId, url.GetUrl(),
desiredSize, contentScale, fittingMode, samplingMode,
false, cropToMask, useAtlas, textureHash, orientationCorrection,
- preMultiply ) );
+ preMultiply, animatedImageLoading, frameIndex ) );
cacheIndex = mTextureInfoContainer.size() - 1u;
- DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d\n",
+ 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 );
}
textureInfo.storageType = storageType;
textureInfo.orientationCorrection = orientationCorrection;
- DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureInfo loadState:%s\n",
- textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
- textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
- textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
- textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
+ 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::LOADING != textureInfo.loadState &&
- TextureManager::CANCELLED != textureInfo.loadState )
+ 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::NOT_STARTED;
+
+ textureInfo.loadState = TextureManager::LoadState::NOT_STARTED;
}
// Check if we should add the observer.
// Only do this if we have not loaded yet and it will not have loaded by the end of this method.
switch( textureInfo.loadState )
{
- case TextureManager::LOAD_FAILED: // Failed notifies observer which then stops observing.
- case TextureManager::NOT_STARTED:
+ case TextureManager::LoadState::LOAD_FAILED: // Failed notifies observer which then stops observing.
+ case TextureManager::LoadState::NOT_STARTED:
{
- LoadTexture( textureInfo );
- ObserveTexture( textureInfo, observer );
+ LoadOrQueueTexture( textureInfo, observer ); // If called inside NotifyObservers, queues until afterwards
break;
}
- case TextureManager::LOADING:
+ 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::UPLOADED:
+ case TextureManager::LoadState::UPLOADED:
{
if( observer )
{
- // The Texture has already loaded. The other observers have already been notified.
- // We need to send a "late" loaded notification for this observer.
- observer->UploadComplete( true, textureInfo.textureId, textureInfo.textureSet,
- textureInfo.useAtlas, textureInfo.atlasRect,
- textureInfo.preMultiplied );
+ LoadOrQueueTexture( textureInfo, observer );
}
break;
}
- case TextureManager::CANCELLED:
+ 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::LOADING;
+ textureInfo.loadState = TextureManager::LoadState::LOADING;
ObserveTexture( textureInfo, observer );
break;
}
- case TextureManager::LOAD_FINISHED:
- case TextureManager::WAITING_FOR_MASK:
- // Loading has already completed. Do nothing.
+ case TextureManager::LoadState::LOAD_FINISHED:
+ {
+ // Loading has already completed.
+ if( observer && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER )
+ {
+ LoadOrQueueTexture( textureInfo, observer );
+ }
break;
+ }
}
// Return the TextureId for which this Texture can now be referenced by externally.
return textureId;
}
-void TextureManager::Remove( const TextureManager::TextureId 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) cacheIdx:%d loadState:%s\n",
- textureId, textureInfoIndex,
- textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
- textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
- textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
- textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
+ 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 )
bool removeTextureInfo = false;
// If loaded, we can remove the TextureInfo and the Atlas (if atlased).
- if( textureInfo.loadState == UPLOADED )
+ if( textureInfo.loadState == LoadState::UPLOADED )
{
if( textureInfo.atlas )
{
}
removeTextureInfo = true;
}
- else if( textureInfo.loadState == LOADING )
+ 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 = CANCELLED;
+ textureInfo.loadState = LoadState::CANCELLED;
}
else
{
mTextureInfoContainer.erase( mTextureInfoContainer.begin() + textureInfoIndex );
}
}
+
+ if( observer )
+ {
+ // Remove element from the LoadQueue
+ for( auto&& element : mLoadQueue )
+ {
+ if( element.mObserver == observer )
+ {
+ mLoadQueue.Erase( &element );
+ break;
+ }
+ }
+ }
}
}
TextureManager::LoadState TextureManager::GetTextureState( TextureId textureId )
{
- LoadState loadState = TextureManager::NOT_STARTED;
+ LoadState loadState = TextureManager::LoadState::NOT_STARTED;
int cacheIndex = GetCacheIndexFromId( textureId );
if( cacheIndex != INVALID_CACHE_INDEX )
TextureManager::LoadState TextureManager::GetTextureStateInternal( TextureId textureId )
{
- LoadState loadState = TextureManager::NOT_STARTED;
+ LoadState loadState = TextureManager::LoadState::NOT_STARTED;
int cacheIndex = GetCacheIndexFromId( textureId );
if( cacheIndex != INVALID_CACHE_INDEX )
return TextureSet();
}
-
void TextureManager::AddObserver( TextureManager::LifecycleObserver& observer )
{
// make sure an observer doesn't observe the same object twice
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->UploadComplete( true, 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;
+ }
+ }
+}
-bool TextureManager::LoadTexture( TextureInfo& textureInfo )
+void TextureManager::QueueLoadTexture( TextureInfo& textureInfo, TextureUploadObserver* observer )
{
- bool success = true;
+ auto textureId = textureInfo.textureId;
+ mLoadQueue.PushBack( LoadQueueElement( textureId, observer) );
- if( textureInfo.loadState == NOT_STARTED )
- {
- textureInfo.loadState = LOADING;
+ 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" );
- if( !textureInfo.loadSynchronously )
+ textureInfo.loadState = LoadState::LOADING;
+ if( !textureInfo.loadSynchronously )
+ {
+ auto& loadersContainer = textureInfo.url.IsLocalResource() ? 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
{
- auto& loadersContainer = textureInfo.url.IsLocalResource() ? mAsyncLocalLoaders : mAsyncRemoteLoaders;
- auto loadingHelperIt = loadersContainer.GetNext();
- DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End());
loadingHelperIt->Load(textureInfo.textureId, textureInfo.url,
textureInfo.desiredSize, textureInfo.fittingMode,
- textureInfo.samplingMode, textureInfo.orientationCorrection );
+ textureInfo.samplingMode, textureInfo.orientationCorrection,
+ premultiplyOnLoad );
}
}
+ ObserveTexture( textureInfo, observer );
+}
- return success;
+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->UploadComplete( true, 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, textureInfo.pixelBuffer, textureInfo.url, 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 );
{
TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
- DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, " CacheIndex:%d LoadState: %d\n", cacheIndex, textureInfo.loadState );
+ 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 != CANCELLED )
+ 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 );
+ Remove( textureInfo.textureId, nullptr );
}
}
}
{
// No atlas support for now
textureInfo.useAtlas = NO_ATLAS;
+ textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied();
- if( textureInfo.storageType == UPLOAD_TO_TEXTURE )
+ 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.
if( textureInfo.maskTextureId != INVALID_TEXTURE_ID )
{
- LoadState maskLoadState = GetTextureStateInternal( textureInfo.maskTextureId );
- if( maskLoadState == LOADING )
- {
- textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily
- textureInfo.loadState = WAITING_FOR_MASK;
- }
- else if( maskLoadState == LOAD_FINISHED )
+ if( textureInfo.loadState == LoadState::MASK_APPLYING )
{
- ApplyMask( pixelBuffer, textureInfo.maskTextureId, textureInfo.scaleFactor, textureInfo.cropToMask );
+ 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
{
else
{
textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data
- textureInfo.loadState = LOAD_FINISHED;
+ textureInfo.loadState = LoadState::LOAD_FINISHED;
- // 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 );
+ 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
{
- DALI_LOG_ERROR( "TextureManager::AsyncImageLoad(%s) failed\n", textureInfo.url.GetUrl().c_str() );
- // @todo If the load was unsuccessful, upload the broken image.
- textureInfo.loadState = LOAD_FAILED;
+ textureInfo.loadState = LoadState::LOAD_FAILED;
CheckForWaitingTexture( textureInfo );
NotifyObservers( textureInfo, false );
}
for( unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex )
{
if( mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
- mTextureInfoContainer[cacheIndex].loadState == WAITING_FOR_MASK )
+ mTextureInfoContainer[cacheIndex].loadState == LoadState::WAITING_FOR_MASK )
{
TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
- Devel::PixelBuffer pixelBuffer = textureInfo.pixelBuffer;
- textureInfo.pixelBuffer.Reset();
- if( maskTextureInfo.loadState == LOAD_FINISHED )
+ if( maskTextureInfo.loadState == LoadState::LOAD_FINISHED )
{
- ApplyMask( pixelBuffer, maskTextureInfo.textureId, textureInfo.scaleFactor, textureInfo.cropToMask );
- UploadTexture( pixelBuffer, textureInfo );
- NotifyObservers( textureInfo, true );
+ // Send New Task to Thread
+ ApplyMask( textureInfo, maskTextureInfo.textureId );
}
else
{
- DALI_LOG_ERROR( "TextureManager::ApplyMask to %s failed\n", textureInfo.url.GetUrl().c_str() );
- textureInfo.loadState = LOAD_FAILED;
+ textureInfo.pixelBuffer.Reset();
+ textureInfo.loadState = LoadState::LOAD_FAILED;
NotifyObservers( textureInfo, false );
}
}
}
}
-void TextureManager::ApplyMask(
- Devel::PixelBuffer& pixelBuffer, TextureId maskTextureId,
- float contentScale, bool cropToMask )
+void TextureManager::ApplyMask( TextureInfo& textureInfo, TextureId maskTextureId )
{
int maskCacheIndex = GetCacheIndexFromId( maskTextureId );
if( maskCacheIndex != INVALID_CACHE_INDEX )
{
Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer;
- pixelBuffer.ApplyMask( maskPixelBuffer, contentScale, cropToMask );
+ 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() ? 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.useAtlas != USE_ATLAS )
{
- DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, " TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId );
+ DALI_LOG_INFO( gTextureManagerLogFilter, Debug::General, " TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId );
- // If the texture doesn't have an alpha channel, can't pre-multiply it.
- // Ensure that we don't change the load parameter (it's used for hashing), and instead set
- // the status for use in the observer.
- auto preMultiply = textureInfo.preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD :
- TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
- PreMultiply( pixelBuffer, preMultiply );
- textureInfo.preMultiplied = (preMultiply == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD );
+ // 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() );
// 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 = UPLOADED;
+ textureInfo.loadState = LoadState::UPLOADED;
}
void TextureManager::NotifyObservers( TextureInfo& textureInfo, bool success )
// If there is an observer: Notify the load is complete, whether successful or not,
// and erase it from the list
- unsigned int observerCount = textureInfo.observerList.Count();
TextureInfo* info = &textureInfo;
- while( observerCount )
+ mQueueLoadFlag = true;
+
+ while( info->observerList.Count() )
{
TextureUploadObserver* observer = info->observerList[0];
// During UploadComplete() a Control ResourceReady() signal is emitted.
// During that signal the app may add remove /add Textures (e.g. via
- // ImageViews). At this point no more observers can be added to the
- // observerList, because textureInfo.loadState = UPLOADED. However it is
- // possible for observers to be removed, hence we check the observer list
- // count every iteration.
-
- // The reference to the textureInfo struct can also become invalidated,
- // because new load requests can modify the mTextureInfoContainer list
- // (e.g. if more requests are pushed back it can cause the list to be
- // resized invalidating the reference to the TextureInfo ).
- observer->UploadComplete( success, info->textureId, info->textureSet, info->useAtlas, info->atlasRect,
- info->preMultiplied );
+ // 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 );
- // Get the textureInfo from the container again as it may have been
- // invalidated,
+ info->observerList.Erase( info->observerList.begin() );
- int textureInfoIndex = GetCacheIndexFromId( textureId );
- if( textureInfoIndex == INVALID_CACHE_INDEX)
+ if( info->storageType == StorageType::RETURN_PIXEL_BUFFER )
{
- return; // texture has been removed - can stop.
+ observer->LoadComplete( success, info->pixelBuffer, info->url, info->preMultiplied );
+ }
+ else
+ {
+ observer->UploadComplete( success, info->textureId, info->textureSet, info->useAtlas, info->atlasRect,
+ info->preMultiplied );
}
- info = &mTextureInfoContainer[ textureInfoIndex ];
- observerCount = info->observerList.Count();
- if ( observerCount > 0 )
+ // Get the textureInfo from the container again as it may have been invalidated.
+ int textureInfoIndex = GetCacheIndexFromId( textureId );
+ if( textureInfoIndex == INVALID_CACHE_INDEX)
{
- // remove the observer that was just triggered if it's still in the list
- for( TextureInfo::ObserverListType::Iterator j = info->observerList.Begin(); j != info->observerList.End(); ++j )
- {
- if( *j == observer )
- {
- info->observerList.Erase( j );
- observerCount--;
- break;
- }
- }
+ 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 );
}
}
const FittingMode::Type fittingMode,
const Dali::SamplingMode::Type samplingMode,
const UseAtlas useAtlas,
- TextureId maskTextureId,
- TextureManager::MultiplyOnLoad preMultiplyOnLoad)
+ TextureId maskTextureId)
{
std::string hashTarget( url );
const size_t urlLength = hashTarget.length();
{
// 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.
- hashTarget[ urlLength ] = useAtlas;
+ switch( useAtlas )
+ {
+ case UseAtlas::NO_ATLAS:
+ {
+ hashTarget[ urlLength ] = 'f';
+ break;
+ }
+ case UseAtlas::USE_ATLAS:
+ {
+ hashTarget[ urlLength ] = 't';
+ break;
+ }
+ }
}
if( maskTextureId != INVALID_TEXTURE_ID )
}
}
- auto premultipliedIndex = hashTarget.length();
- hashTarget.resize( premultipliedIndex + 1 );
- switch( preMultiplyOnLoad )
- {
- case TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD:
- {
- hashTarget[ premultipliedIndex ] = 't';
- break;
- }
- case TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY:
- {
- hashTarget[ premultipliedIndex ] = 'f';
- break;
- }
- }
-
return Dali::CalculateHash( hashTarget );
}
const Dali::SamplingMode::Type samplingMode,
const bool useAtlas,
TextureId maskTextureId,
- TextureManager::MultiplyOnLoad preMultiplyOnLoad )
+ TextureManager::MultiplyOnLoad preMultiplyOnLoad)
{
// Default to an invalid ID, in case we do not find a match.
int cacheIndex = INVALID_CACHE_INDEX;
{
// We have a match, now we check all the original parameters in case of a hash collision.
TextureInfo& textureInfo( mTextureInfoContainer[i] );
- auto multiplyOnLoad = textureInfo.preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD :
- TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
if( ( url == textureInfo.url.GetUrl() ) &&
( useAtlas == textureInfo.useAtlas ) &&
( maskTextureId == textureInfo.maskTextureId ) &&
( size == textureInfo.desiredSize ) &&
- ( preMultiplyOnLoad == multiplyOnLoad ) &&
( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
( fittingMode == textureInfo.fittingMode &&
samplingMode == textureInfo.samplingMode ) ) )
{
- // The found Texture is a match.
- cacheIndex = i;
- break;
+ // 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;
+ }
}
}
}
}
}
}
+
+ // Remove element from the LoadQueue
+ for( auto&& element : mLoadQueue )
+ {
+ if( element.mObserver == observer )
+ {
+ element.mObserver = nullptr;
+ }
+ }
}
{
}
-void TextureManager::AsyncLoadingHelper::Load(TextureId textureId,
- const VisualUrl& url,
- ImageDimensions desiredSize,
- FittingMode::Type fittingMode,
- SamplingMode::Type samplingMode,
- bool orientationCorrection)
+void TextureManager::AsyncLoadingHelper::LoadAnimatedImage( TextureId textureId,
+ Dali::AnimatedImageLoading animatedImageLoading,
+ uint32_t frameIndex )
+{
+ mLoadingInfoContainer.push_back( AsyncLoadingInfo( textureId ) );
+ auto id = DevelAsyncImageLoader::LoadAnimatedImage( mLoader, 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 ) );
+ auto id = DevelAsyncImageLoader::Load( mLoader, url.GetUrl(), 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 = mLoader.Load(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
+ mLoadingInfoContainer.push_back( AsyncLoadingInfo( textureId ) );
+ auto id = DevelAsyncImageLoader::ApplyMask( mLoader, pixelBuffer, maskPixelBuffer, contentScale, cropToMask, preMultiplyOnLoad );
mLoadingInfoContainer.back().loadId = id;
}
}
void TextureManager::AsyncLoadingHelper::AsyncLoadComplete(uint32_t id,
- Devel::PixelBuffer pixelBuffer)
+ Devel::PixelBuffer pixelBuffer )
{
- mTextureManager.AsyncLoadComplete(mLoadingInfoContainer, id, pixelBuffer);
+ mTextureManager.AsyncLoadComplete( mLoadingInfoContainer, id, pixelBuffer );
}
void TextureManager::SetBrokenImageUrl(const std::string& brokenImageUrl)
mBrokenImageUrl = brokenImageUrl;
}
+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