X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fvisuals%2Ftexture-manager-impl.cpp;h=9a06862d1d2e13b13adc449fa12005b269226167;hp=c867b91d2a08b1f3e1d83f1f8cd990bafc37133e;hb=d217647be996b50bf1582174ec1dad3b61a919e1;hpb=935ef82404ba71238cfa58b8938ac325604dd7d6 diff --git a/dali-toolkit/internal/visuals/texture-manager-impl.cpp b/dali-toolkit/internal/visuals/texture-manager-impl.cpp index c867b91..9a06862 100644 --- a/dali-toolkit/internal/visuals/texture-manager-impl.cpp +++ b/dali-toolkit/internal/visuals/texture-manager-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 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. @@ -49,8 +49,8 @@ size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue) auto numberString = GetEnvironmentVariable(environmentVariable); auto numberOfThreads = numberString ? std::strtoul(numberString, nullptr, 10) : 0; constexpr auto MAX_NUMBER_OF_THREADS = 100u; - DALI_ASSERT_ALWAYS( numberOfThreads < MAX_NUMBER_OF_THREADS ); - return (numberOfThreads > 0) ? numberOfThreads : defaultValue; + DALI_ASSERT_DEBUG( numberOfThreads < MAX_NUMBER_OF_THREADS ); + return ( numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS ) ? numberOfThreads : defaultValue; } size_t GetNumberOfLocalLoaderThreads() @@ -79,14 +79,41 @@ namespace #ifdef DEBUG_ENABLED Debug::Filter* gTextureManagerLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_TEXTURE_MANAGER" ); + +#define GET_LOAD_STATE_STRING( loadState ) \ + loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" : \ + loadState == TextureManager::LOADING ? "LOADING" : \ + loadState == TextureManager::LOAD_FINISHED ? "LOAD_FINISHED" : \ + loadState == TextureManager::WAITING_FOR_MASK ? "WAITING_FOR_MASK" : \ + loadState == TextureManager::MASK_APPLYING ? "MASK_APPLYING" : \ + loadState == TextureManager::MASK_APPLIED ? "MASK_APPLIED" : \ + loadState == TextureManager::UPLOADED ? "UPLOADED" : \ + loadState == TextureManager::CANCELLED ? "CANCELLED" : \ + loadState == TextureManager::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 char * const BROKEN_IMAGE_URL( DALI_IMAGE_DIR "broken.png" ); ///< URL For the broken image placeholder 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() @@ -100,18 +127,31 @@ TextureManager::MaskingData::MaskingData() TextureManager::TextureManager() : mAsyncLocalLoaders( GetNumberOfLocalLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ), mAsyncRemoteLoaders( GetNumberOfRemoteLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ), - mCurrentTextureId( 0 ) + mExternalTextures(), + mLifecycleObservers(), + mLoadQueue(), + mBrokenImageUrl(""), + mCurrentTextureId( 0 ), + mQueueLoadFlag(false) +{ +} + +TextureManager::~TextureManager() { + for( auto iter = mLifecycleObservers.Begin(), endIter = mLifecycleObservers.End(); iter != endIter; ++iter) + { + (*iter)->TextureManagerDestroyed(); + } } TextureSet TextureManager::LoadTexture( - VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode, - Dali::SamplingMode::Type samplingMode, const MaskingDataPointer& maskInfo, - bool synchronousLoading, TextureManager::TextureId& textureId, Vector4& textureRect, - bool& atlasingStatus, bool& loadingStatus, Dali::WrapMode::Type wrapModeU, - Dali::WrapMode::Type wrapModeV, TextureUploadObserver* textureObserver, - AtlasUploadObserver* atlasObserver, ImageAtlasManagerPtr imageAtlasManager, bool orientationCorrection, - TextureManager::ReloadPolicy reloadPolicy ) + const VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode, + Dali::SamplingMode::Type samplingMode, const 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; @@ -128,6 +168,8 @@ TextureSet TextureManager::LoadTexture( { if( elem.textureId == id ) { + preMultiplyOnLoad = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + textureId = elem.textureId; return elem.textureSet; } } @@ -142,6 +184,7 @@ TextureSet TextureManager::LoadTexture( orientationCorrection ); if( pixelBuffer ) { + PreMultiply( pixelBuffer, preMultiplyOnLoad ); data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer } } @@ -149,9 +192,10 @@ TextureSet TextureManager::LoadTexture( { // use broken image textureSet = TextureSet::New(); - Devel::PixelBuffer pixelBuffer = LoadImageFromFile( BROKEN_IMAGE_URL ); + Devel::PixelBuffer pixelBuffer = LoadImageFromFile( mBrokenImageUrl ); if( pixelBuffer ) { + PreMultiply( pixelBuffer, preMultiplyOnLoad ); data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer } Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(), @@ -175,6 +219,11 @@ TextureSet TextureManager::LoadTexture( textureSet = TextureSet::New(); textureSet.SetTexture( 0u, texture ); } + else + { + textureRectSize.SetWidth(data.GetWidth()); + textureRectSize.SetHeight(data.GetHeight()); + } } } else @@ -182,7 +231,7 @@ TextureSet TextureManager::LoadTexture( loadingStatus = true; if( atlasingStatus ) { - textureSet = imageAtlasManager->Add( textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver ); + textureSet = imageAtlasManager->Add( textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver); } if( !textureSet ) // big image, no atlasing or atlasing failed { @@ -190,7 +239,7 @@ TextureSet TextureManager::LoadTexture( if( !maskInfo ) { textureId = RequestLoad( url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, - textureObserver, orientationCorrection, reloadPolicy ); + textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad ); } else { @@ -203,17 +252,29 @@ TextureSet TextureManager::LoadTexture( maskInfo->mCropToMask, textureObserver, orientationCorrection, - reloadPolicy ); + reloadPolicy, preMultiplyOnLoad ); } - TextureManager::LoadState loadState = GetTextureState( textureId ); - loadingStatus = ( loadState == TextureManager::LOADING ); - + TextureManager::LoadState loadState = GetTextureStateInternal( textureId ); if( loadState == TextureManager::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::LOADING || + loadState == TextureManager::WAITING_FOR_MASK || + loadState == TextureManager::MASK_APPLYING || + loadState == TextureManager::MASK_APPLIED || + loadState == TextureManager::NOT_STARTED || + mQueueLoadFlag ); + + } + else + { + textureRectSize = desiredSize; } } @@ -228,46 +289,51 @@ TextureSet TextureManager::LoadTexture( } 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 ) + 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 ) { return RequestLoadInternal( url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, - false, UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy ); + false, UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, + preMultiplyOnLoad ); } 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 ) + 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 ) { return RequestLoadInternal( url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, - cropToMask, UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy ); + cropToMask, UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, + preMultiplyOnLoad ); } 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 ); + TextureManager::ReloadPolicy::CACHED, preMultiply ); } TextureManager::TextureId TextureManager::RequestLoadInternal( - const VisualUrl& url, + const VisualUrl& url, TextureId maskTextureId, float contentScale, const ImageDimensions desiredSize, @@ -278,7 +344,8 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( StorageType storageType, TextureUploadObserver* observer, bool orientationCorrection, - TextureManager::ReloadPolicy reloadPolicy ) + TextureManager::ReloadPolicy reloadPolicy, + TextureManager::MultiplyOnLoad& preMultiplyOnLoad) { // First check if the requested Texture is cached. const TextureHash textureHash = GenerateHash( url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, @@ -288,7 +355,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( // 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 ); + maskTextureId, preMultiplyOnLoad ); // Check if the requested Texture exists in the cache. if( cacheIndex != INVALID_CACHE_INDEX ) @@ -300,7 +367,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( ++( 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", + 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 ); } @@ -308,12 +375,14 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( { // We need a new Texture. textureId = GenerateUniqueTextureId(); + bool preMultiply = ( preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD ); mTextureInfoContainer.push_back( TextureInfo( textureId, maskTextureId, url.GetUrl(), desiredSize, contentScale, fittingMode, samplingMode, - false, cropToMask, useAtlas, textureHash, orientationCorrection ) ); + false, cropToMask, useAtlas, textureHash, orientationCorrection, + preMultiply ) ); 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 ); } @@ -325,14 +394,15 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( 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 && + if ( TextureManager::ReloadPolicy::FORCED == reloadPolicy && + TextureManager::LOADING != textureInfo.loadState && + TextureManager::WAITING_FOR_MASK != textureInfo.loadState && + TextureManager::MASK_APPLYING != textureInfo.loadState && + TextureManager::MASK_APPLIED != textureInfo.loadState && TextureManager::CANCELLED != textureInfo.loadState ) { DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d\n", @@ -347,11 +417,13 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( case TextureManager::LOAD_FAILED: // Failed notifies observer which then stops observing. case TextureManager::NOT_STARTED: { - LoadTexture( textureInfo ); - ObserveTexture( textureInfo, observer ); + LoadOrQueueTexture( textureInfo, observer ); // If called inside NotifyObservers, queues until afterwards break; } case TextureManager::LOADING: + case TextureManager::WAITING_FOR_MASK: + case TextureManager::MASK_APPLYING: + case TextureManager::MASK_APPLIED: { ObserveTexture( textureInfo, observer ); break; @@ -360,10 +432,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( { 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 ); + LoadOrQueueTexture( textureInfo, observer ); } break; } @@ -376,7 +445,6 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( break; } case TextureManager::LOAD_FINISHED: - case TextureManager::WAITING_FOR_MASK: // Loading has already completed. Do nothing. break; } @@ -392,12 +460,10 @@ void TextureManager::Remove( const TextureManager::TextureId textureId ) { 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\n", + textureId, textureInfo.url.GetUrl().c_str(), + textureInfoIndex, GET_LOAD_STATE_STRING( textureInfo.loadState ) ); // Decrement the reference count and check if this is the last user of this Texture. if( --textureInfo.referenceCount <= 0 ) @@ -437,13 +503,20 @@ void TextureManager::Remove( const TextureManager::TextureId textureId ) } } -const VisualUrl& TextureManager::GetVisualUrl( TextureId textureId ) +VisualUrl TextureManager::GetVisualUrl( TextureId textureId ) { + VisualUrl visualUrl(""); int cacheIndex = GetCacheIndexFromId( textureId ); - DALI_ASSERT_DEBUG( cacheIndex != INVALID_CACHE_INDEX && "TextureId out of range"); - TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] ); - return cachedTextureInfo.url; + 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 ) @@ -456,6 +529,31 @@ TextureManager::LoadState TextureManager::GetTextureState( TextureId textureId ) 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::NOT_STARTED; + + int cacheIndex = GetCacheIndexFromId( textureId ); + if( cacheIndex != INVALID_CACHE_INDEX ) + { + TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] ); + loadState = cachedTextureInfo.loadState; + } + return loadState; } @@ -469,6 +567,17 @@ TextureSet TextureManager::GetTextureSet( TextureId textureId ) TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] ); textureSet = cachedTextureInfo.textureSet; } + else + { + for( auto&& elem : mExternalTextures ) + { + if( elem.textureId == textureId ) + { + textureSet = elem.textureSet; + break; + } + } + } return textureSet; } @@ -509,31 +618,130 @@ TextureSet TextureManager::RemoveExternalTexture( const std::string& url ) return TextureSet(); } -bool TextureManager::LoadTexture( TextureInfo& textureInfo ) + +void TextureManager::AddObserver( TextureManager::LifecycleObserver& observer ) { - bool success = true; + // 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 ); +} - if( textureInfo.loadState == NOT_STARTED ) +void TextureManager::RemoveObserver( TextureManager::LifecycleObserver& observer) +{ + // Find the observer... + auto endIter = mLifecycleObservers.End(); + for( auto iter = mLifecycleObservers.Begin(); iter != endIter; ++iter) { - textureInfo.loadState = LOADING; + if( (*iter) == &observer) + { + mLifecycleObservers.Erase( iter ); + break; + } + } + DALI_ASSERT_DEBUG(endIter != mLifecycleObservers.End()); +} - if( !textureInfo.loadSynchronously ) +void TextureManager::LoadOrQueueTexture( TextureInfo& textureInfo, TextureUploadObserver* observer ) +{ + switch( textureInfo.loadState ) + { + case NOT_STARTED: + case LOAD_FAILED: { - 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 ); + if( mQueueLoadFlag ) + { + QueueLoadTexture( textureInfo, observer ); + } + else + { + LoadTexture( textureInfo, observer ); + } + break; } + case 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 LOADING: + case CANCELLED: + case LOAD_FINISHED: + case WAITING_FOR_MASK: + case MASK_APPLYING: + case MASK_APPLIED: + { + break; + } + } +} + +void TextureManager::QueueLoadTexture( TextureInfo& textureInfo, TextureUploadObserver* observer ) +{ + auto textureId = textureInfo.textureId; + mLoadQueue.PushBack( LoadQueueElement( textureId, observer) ); +} + +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 = LOADING; + if( !textureInfo.loadSynchronously ) + { + 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->Load(textureInfo.textureId, textureInfo.url, + textureInfo.desiredSize, textureInfo.fittingMode, + textureInfo.samplingMode, textureInfo.orientationCorrection, + premultiplyOnLoad ); } + ObserveTexture( textureInfo, observer ); +} - return success; +void TextureManager::ProcessQueuedTextures() +{ + for( auto&& element : mLoadQueue ) + { + int cacheIndex = GetCacheIndexFromId( element.mTextureId ); + if( cacheIndex != INVALID_CACHE_INDEX ) + { + TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] ); + if( textureInfo.loadState == UPLOADED ) + { + element.mObserver->UploadComplete( true, textureInfo.textureId, textureInfo.textureSet, + textureInfo.useAtlas, textureInfo.atlasRect, + 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 ); @@ -557,7 +765,9 @@ void TextureManager::AsyncLoadComplete( AsyncLoadingInfoContainerType& loadingCo { 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 ) { @@ -590,18 +800,26 @@ void TextureManager::PostLoad( TextureInfo& textureInfo, Devel::PixelBuffer& pix // wait for the mask to finish loading. if( textureInfo.maskTextureId != INVALID_TEXTURE_ID ) { - LoadState maskLoadState = GetTextureState( textureInfo.maskTextureId ); - if( maskLoadState == LOADING ) + if( textureInfo.loadState == MASK_APPLYING ) { - textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily - textureInfo.loadState = WAITING_FOR_MASK; - } - else if( maskLoadState == LOAD_FINISHED ) - { - ApplyMask( pixelBuffer, textureInfo.maskTextureId, textureInfo.scaleFactor, textureInfo.cropToMask ); + textureInfo.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 == LOADING ) + { + textureInfo.loadState = WAITING_FOR_MASK; + } + else if( maskLoadState == LOAD_FINISHED ) + { + // Send New Task to Thread + ApplyMask( textureInfo, textureInfo.maskTextureId ); + } + } } else { @@ -621,7 +839,6 @@ void TextureManager::PostLoad( TextureInfo& textureInfo, Devel::PixelBuffer& pix } 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; CheckForWaitingTexture( textureInfo ); @@ -641,18 +858,15 @@ void TextureManager::CheckForWaitingTexture( TextureInfo& maskTextureInfo ) mTextureInfoContainer[cacheIndex].loadState == WAITING_FOR_MASK ) { TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] ); - Devel::PixelBuffer pixelBuffer = textureInfo.pixelBuffer; - textureInfo.pixelBuffer.Reset(); if( maskTextureInfo.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.pixelBuffer.Reset(); textureInfo.loadState = LOAD_FAILED; NotifyObservers( textureInfo, false ); } @@ -660,23 +874,38 @@ void TextureManager::CheckForWaitingTexture( TextureInfo& maskTextureInfo ) } } -void TextureManager::ApplyMask( - Devel::PixelBuffer& pixelBuffer, TextureId maskTextureId, - float contentScale, bool cropToMask ) +void TextureManager::ApplyMask( TextureInfo& textureInfo, TextureId maskTextureId ) { int maskCacheIndex = GetCacheIndexFromId( maskTextureId ); - Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer; - pixelBuffer.ApplyMask( maskPixelBuffer, contentScale, cropToMask ); + 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 = MASK_APPLYING; + auto& loadersContainer = textureInfo.url.IsLocalResource() ? mAsyncLocalLoaders : mAsyncRemoteLoaders; + auto loadingHelperIt = loadersContainer.GetNext(); + DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End()); + loadingHelperIt->ApplyMask( textureInfo.textureId, pixelBuffer, maskPixelBuffer, textureInfo.scaleFactor, textureInfo.cropToMask ); + } } 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 ); + + // Check if this pixelBuffer is premultiplied + textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied(); 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 ) @@ -699,52 +928,50 @@ 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 ); + // 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 ) ); + + observer->UploadComplete( success, info->textureId, info->textureSet, info->useAtlas, info->atlasRect, + info->preMultiplied ); observer->DestructionSignal().Disconnect( this, &TextureManager::ObserverDestroyed ); - // Get the textureInfo from the container again as it may have been - // invalidated, - + // Get the textureInfo from the container again as it may have been invalidated. int textureInfoIndex = GetCacheIndexFromId( textureId ); if( textureInfoIndex == INVALID_CACHE_INDEX) { - return; // texture has been removed - can stop. + break; // texture has been removed - can stop. } - info = &mTextureInfoContainer[ textureInfoIndex ]; - observerCount = info->observerList.Count(); - if ( observerCount > 0 ) + + // 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 ) { - // 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 ) { - if( *j == observer ) - { - info->observerList.Erase( j ); - observerCount--; - break; - } + info->observerList.Erase( j ); + break; } } } + + mQueueLoadFlag = false; + ProcessQueuedTextures(); } TextureManager::TextureId TextureManager::GenerateUniqueTextureId() @@ -764,7 +991,6 @@ int TextureManager::GetCacheIndexFromId( const TextureId textureId ) } } - DALI_LOG_WARNING( "Cannot locate TextureId: %d\n", textureId ); return INVALID_CACHE_INDEX; } @@ -802,16 +1028,30 @@ TextureManager::TextureHash TextureManager::GenerateHash( { // 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 ) { - hashTarget.resize( urlLength + sizeof( TextureId ) ); - TextureId* hashTargetPtr = reinterpret_cast(&( hashTarget[ urlLength ] )); + auto textureIdIndex = hashTarget.length(); + hashTarget.resize( hashTarget.length() + sizeof( TextureId ) ); + unsigned char* hashTargetPtr = reinterpret_cast(&( hashTarget[ textureIdIndex ] )); - // Append the hash target to the end of the URL byte by byte: + // 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 ) { @@ -830,7 +1070,8 @@ int TextureManager::FindCachedTexture( const FittingMode::Type fittingMode, const Dali::SamplingMode::Type samplingMode, const bool useAtlas, - TextureId maskTextureId) + TextureId maskTextureId, + TextureManager::MultiplyOnLoad preMultiplyOnLoad ) { // Default to an invalid ID, in case we do not find a match. int cacheIndex = INVALID_CACHE_INDEX; @@ -852,9 +1093,15 @@ int TextureManager::FindCachedTexture( ( 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; + } } } } @@ -883,6 +1130,7 @@ void TextureManager::ObserverDestroyed( TextureUploadObserver* observer ) } } + TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(TextureManager& textureManager) : AsyncLoadingHelper(Toolkit::AsyncImageLoader::New(), textureManager, AsyncLoadingInfoContainerType()) @@ -894,10 +1142,22 @@ void TextureManager::AsyncLoadingHelper::Load(TextureId textureId, ImageDimensions desiredSize, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, - bool orientationCorrection) + bool orientationCorrection, + DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad) { mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId)); - auto id = mLoader.Load(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection); + 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 ) +{ + mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId)); + auto id = DevelAsyncImageLoader::ApplyMask( mLoader, pixelBuffer, maskPixelBuffer, contentScale, cropToMask ); mLoadingInfoContainer.back().loadId = id; } @@ -919,9 +1179,14 @@ TextureManager::AsyncLoadingHelper::AsyncLoadingHelper( } void TextureManager::AsyncLoadingHelper::AsyncLoadComplete(uint32_t id, - Devel::PixelBuffer pixelBuffer) + Devel::PixelBuffer pixelBuffer ) +{ + mTextureManager.AsyncLoadComplete( mLoadingInfoContainer, id, pixelBuffer ); +} + +void TextureManager::SetBrokenImageUrl(const std::string& brokenImageUrl) { - mTextureManager.AsyncLoadComplete(mLoadingInfoContainer, id, pixelBuffer); + mBrokenImageUrl = brokenImageUrl; } } // namespace Internal