2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/visuals/texture-manager-impl.h>
24 #include <dali/devel-api/adaptor-framework/environment-variable.h>
25 #include <dali/devel-api/common/hash.h>
26 #include <dali/devel-api/images/texture-set-image.h>
27 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
28 #include <dali/integration-api/debug.h>
31 #include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
32 #include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
37 constexpr auto DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS = size_t{4u};
38 constexpr auto DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS = size_t{8u};
40 constexpr auto NUMBER_OF_LOCAL_LOADER_THREADS_ENV = "DALI_TEXTURE_LOCAL_THREADS";
41 constexpr auto NUMBER_OF_REMOTE_LOADER_THREADS_ENV = "DALI_TEXTURE_REMOTE_THREADS";
43 size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue)
45 using Dali::EnvironmentVariable::GetEnvironmentVariable;
46 auto numberString = GetEnvironmentVariable(environmentVariable);
47 auto numberOfThreads = numberString ? std::strtol(numberString, nullptr, 10) : 0;
48 return (numberOfThreads > 0) ? numberOfThreads : defaultValue;
51 size_t GetNumberOfLocalLoaderThreads()
53 return GetNumberOfThreads(NUMBER_OF_LOCAL_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS);
56 size_t GetNumberOfRemoteLoaderThreads()
58 return GetNumberOfThreads(NUMBER_OF_REMOTE_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS);
76 Debug::Filter* gTextureManagerLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_TEXTURE_MANAGER" );
79 const uint32_t DEFAULT_ATLAS_SIZE( 1024u ); ///< This size can fit 8 by 8 images of average size 128 * 128
80 const Vector4 FULL_ATLAS_RECT( 0.0f, 0.0f, 1.0f, 1.0f ); ///< UV Rectangle that covers the full Texture
81 const char * const BROKEN_IMAGE_URL( DALI_IMAGE_DIR "broken.png" ); ///< URL For the broken image placeholder
82 const int INVALID_INDEX( -1 ); ///< Invalid index used to represent a non-existant TextureInfo struct
83 const int INVALID_CACHE_INDEX( -1 ); ///< Invalid Cache index
85 } // Anonymous namespace
88 TextureManager::TextureManager()
89 : mAsyncLocalLoaders( GetNumberOfLocalLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ),
90 mAsyncRemoteLoaders( GetNumberOfRemoteLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ),
91 mCurrentTextureId( 0 )
95 TextureManager::TextureId TextureManager::RequestLoad(
97 const ImageDimensions desiredSize,
98 FittingMode::Type fittingMode,
99 Dali::SamplingMode::Type samplingMode,
100 const UseAtlas useAtlas,
101 TextureUploadObserver* observer )
103 return RequestLoadInternal( url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, UPLOAD_TO_TEXTURE, observer );
106 TextureManager::TextureId TextureManager::RequestLoad(
107 const VisualUrl& url,
108 TextureId maskTextureId,
110 const ImageDimensions desiredSize,
111 FittingMode::Type fittingMode,
112 Dali::SamplingMode::Type samplingMode,
113 const UseAtlas useAtlas,
115 TextureUploadObserver* observer )
117 return RequestLoadInternal( url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, UPLOAD_TO_TEXTURE, observer );
120 TextureManager::TextureId TextureManager::RequestMaskLoad( const VisualUrl& maskUrl )
122 // Use the normal load procedure to get the alpha mask.
123 return RequestLoadInternal( maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, KEEP_PIXEL_BUFFER, NULL );
127 TextureManager::TextureId TextureManager::RequestLoadInternal(
128 const VisualUrl& url,
129 TextureId maskTextureId,
131 const ImageDimensions desiredSize,
132 FittingMode::Type fittingMode,
133 Dali::SamplingMode::Type samplingMode,
136 StorageType storageType,
137 TextureUploadObserver* observer )
139 // First check if the requested Texture is cached.
140 const TextureHash textureHash = GenerateHash( url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId );
142 TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
144 // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
145 int cacheIndex = FindCachedTexture( textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId );
147 // Check if the requested Texture exists in the cache.
148 if( cacheIndex != INVALID_CACHE_INDEX )
150 // Mark this texture being used by another client resource.
151 ++( mTextureInfoContainer[ cacheIndex ].referenceCount );
152 textureId = mTextureInfoContainer[ cacheIndex ].textureId;
154 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture @%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId );
157 if( textureId == INVALID_TEXTURE_ID ) // There was no caching, or caching not required
159 // We need a new Texture.
160 textureId = GenerateUniqueTextureId();
161 mTextureInfoContainer.push_back( TextureInfo( textureId, maskTextureId, url.GetUrl(),
162 desiredSize, contentScale, fittingMode, samplingMode,
163 false, cropToMask, useAtlas, textureHash ) );
164 cacheIndex = mTextureInfoContainer.size() - 1u;
166 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId );
169 // The below code path is common whether we are using the cache or not.
170 // The textureInfoIndex now refers to either a pre-existing cached TextureInfo,
171 // or a new TextureInfo just created.
172 TextureInfo& textureInfo( mTextureInfoContainer[ cacheIndex ] );
173 textureInfo.maskTextureId = maskTextureId;
174 textureInfo.storageType = storageType;
176 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureInfo loadState:%s\n",
177 textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
178 textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
179 textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
180 textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
182 // 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.
183 switch( textureInfo.loadState )
185 case TextureManager::NOT_STARTED:
187 LoadTexture( textureInfo );
188 ObserveTexture( textureInfo, observer );
191 case TextureManager::LOADING:
193 ObserveTexture( textureInfo, observer );
196 case TextureManager::UPLOADED:
200 // The Texture has already loaded. The other observers have already been notified.
201 // We need to send a "late" loaded notification for this observer.
202 observer->UploadComplete( true, textureInfo.textureId, textureInfo.textureSet,
203 textureInfo.useAtlas, textureInfo.atlasRect );
207 case TextureManager::CANCELLED:
209 // A cancelled texture hasn't finished loading yet. Treat as a loading texture
210 // (it's ref count has already been incremented, above)
211 textureInfo.loadState = TextureManager::LOADING;
212 ObserveTexture( textureInfo, observer );
215 case TextureManager::LOAD_FINISHED:
216 case TextureManager::WAITING_FOR_MASK:
217 case TextureManager::LOAD_FAILED:
218 // Loading has already completed. Do nothing.
222 // Return the TextureId for which this Texture can now be referenced by externally.
226 void TextureManager::Remove( const TextureManager::TextureId textureId )
228 int textureInfoIndex = GetCacheIndexFromId( textureId );
229 if( textureInfoIndex != INVALID_INDEX )
231 TextureInfo& textureInfo( mTextureInfoContainer[ textureInfoIndex ] );
233 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::Remove(%d) cacheIdx:%d loadState:%s\n",
234 textureId, textureInfoIndex,
235 textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
236 textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
237 textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
238 textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
240 // Decrement the reference count and check if this is the last user of this Texture.
241 if( --textureInfo.referenceCount <= 0 )
243 // This is the last remove for this Texture.
244 textureInfo.referenceCount = 0;
245 bool removeTextureInfo = false;
247 // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
248 if( textureInfo.loadState == UPLOADED )
250 if( textureInfo.atlas )
252 textureInfo.atlas.Remove( textureInfo.atlasRect );
254 removeTextureInfo = true;
256 else if( textureInfo.loadState == LOADING )
258 // We mark the textureInfo for removal.
259 // Once the load has completed, this method will be called again.
260 textureInfo.loadState = CANCELLED;
264 // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
265 removeTextureInfo = true;
268 // If the state allows us to remove the TextureInfo data, we do so.
269 if( removeTextureInfo )
271 // Permanently remove the textureInfo struct.
272 mTextureInfoContainer.erase( mTextureInfoContainer.begin() + textureInfoIndex );
278 const VisualUrl& TextureManager::GetVisualUrl( TextureId textureId )
280 int cacheIndex = GetCacheIndexFromId( textureId );
281 DALI_ASSERT_DEBUG( cacheIndex != INVALID_CACHE_INDEX && "TextureId out of range");
283 TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
284 return cachedTextureInfo.url;
287 TextureManager::LoadState TextureManager::GetTextureState( TextureId textureId )
289 LoadState loadState = TextureManager::NOT_STARTED;
291 int cacheIndex = GetCacheIndexFromId( textureId );
292 if( cacheIndex != INVALID_CACHE_INDEX )
294 TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
295 loadState = cachedTextureInfo.loadState;
300 TextureSet TextureManager::GetTextureSet( TextureId textureId )
302 TextureSet textureSet;// empty handle
304 int cacheIndex = GetCacheIndexFromId( textureId );
305 if( cacheIndex != INVALID_CACHE_INDEX )
307 TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
308 textureSet = cachedTextureInfo.textureSet;
313 std::string TextureManager::AddExternalTexture( TextureSet& textureSet )
315 TextureManager::ExternalTextureInfo info;
316 info.textureId = GenerateUniqueTextureId();
317 info.textureSet = textureSet;
318 mExternalTextures.emplace_back( info );
319 return VisualUrl::CreateTextureUrl( std::to_string( info.textureId ) );
322 TextureSet TextureManager::RemoveExternalTexture( const std::string& url )
324 if( url.size() > 0u )
326 // get the location from the Url
327 VisualUrl parseUrl( url );
328 if( VisualUrl::TEXTURE == parseUrl.GetProtocolType() )
330 std::string location = parseUrl.GetLocation();
331 if( location.size() > 0u )
333 TextureId id = std::stoi( location );
334 const auto end = mExternalTextures.end();
335 for( auto iter = mExternalTextures.begin(); iter != end; ++iter )
337 if( iter->textureId == id )
339 auto textureSet = iter->textureSet;
340 mExternalTextures.erase( iter );
350 bool TextureManager::LoadTexture( TextureInfo& textureInfo )
354 if( textureInfo.loadState == NOT_STARTED )
356 textureInfo.loadState = LOADING;
358 if( !textureInfo.loadSynchronously )
360 auto& loadersContainer = textureInfo.url.IsLocalResource() ? mAsyncLocalLoaders : mAsyncRemoteLoaders;
361 auto loadingHelperIt = loadersContainer.GetNext();
362 DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End());
363 loadingHelperIt->Load(textureInfo.textureId, textureInfo.url,
364 textureInfo.desiredSize, textureInfo.fittingMode,
365 textureInfo.samplingMode, true);
372 void TextureManager::ObserveTexture( TextureInfo& textureInfo,
373 TextureUploadObserver* observer )
377 textureInfo.observerList.PushBack( observer );
378 observer->DestructionSignal().Connect( this, &TextureManager::ObserverDestroyed );
382 void TextureManager::AsyncLoadComplete( AsyncLoadingInfoContainerType& loadingContainer, uint32_t id, Devel::PixelBuffer pixelBuffer )
384 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( id:%d )\n", id );
386 if( loadingContainer.size() >= 1u )
388 AsyncLoadingInfo loadingInfo = loadingContainer.front();
390 if( loadingInfo.loadId == id )
392 int cacheIndex = GetCacheIndexFromId( loadingInfo.textureId );
393 if( cacheIndex != INVALID_CACHE_INDEX )
395 TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
397 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, " CacheIndex:%d LoadState: %d\n", cacheIndex, textureInfo.loadState );
399 if( textureInfo.loadState != CANCELLED )
401 // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified)
402 PostLoad( textureInfo, pixelBuffer );
406 Remove( textureInfo.textureId );
411 loadingContainer.pop_front();
415 void TextureManager::PostLoad( TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer )
417 // Was the load successful?
418 if( pixelBuffer && ( pixelBuffer.GetWidth() != 0 ) && ( pixelBuffer.GetHeight() != 0 ) )
420 // No atlas support for now
421 textureInfo.useAtlas = NO_ATLAS;
423 if( textureInfo.storageType == UPLOAD_TO_TEXTURE )
425 // If there is a mask texture ID associated with this texture, then apply the mask
426 // if it's already loaded. If it hasn't, and the mask is still loading,
427 // wait for the mask to finish loading.
428 if( textureInfo.maskTextureId != INVALID_TEXTURE_ID )
430 LoadState maskLoadState = GetTextureState( textureInfo.maskTextureId );
431 if( maskLoadState == LOADING )
433 textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily
434 textureInfo.loadState = WAITING_FOR_MASK;
436 else if( maskLoadState == LOAD_FINISHED )
438 ApplyMask( pixelBuffer, textureInfo.maskTextureId, textureInfo.scaleFactor, textureInfo.cropToMask );
439 UploadTexture( pixelBuffer, textureInfo );
440 NotifyObservers( textureInfo, true );
445 UploadTexture( pixelBuffer, textureInfo );
446 NotifyObservers( textureInfo, true );
451 textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data
452 textureInfo.loadState = LOAD_FINISHED;
454 // Check if there was another texture waiting for this load to complete
455 // (e.g. if this was an image mask, and its load is on a different thread)
456 CheckForWaitingTexture( textureInfo );
461 DALI_LOG_ERROR( "TextureManager::AsyncImageLoad(%s) failed\n", textureInfo.url.GetUrl().c_str() );
462 // @todo If the load was unsuccessful, upload the broken image.
463 textureInfo.loadState = LOAD_FAILED;
464 CheckForWaitingTexture( textureInfo );
465 NotifyObservers( textureInfo, false );
469 void TextureManager::CheckForWaitingTexture( TextureInfo& maskTextureInfo )
471 // Search the cache, checking if any texture has this texture id as a
473 const unsigned int size = mTextureInfoContainer.size();
475 for( unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex )
477 if( mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
478 mTextureInfoContainer[cacheIndex].loadState == WAITING_FOR_MASK )
480 TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
481 Devel::PixelBuffer pixelBuffer = textureInfo.pixelBuffer;
482 textureInfo.pixelBuffer.Reset();
484 if( maskTextureInfo.loadState == LOAD_FINISHED )
486 ApplyMask( pixelBuffer, maskTextureInfo.textureId, textureInfo.scaleFactor, textureInfo.cropToMask );
487 UploadTexture( pixelBuffer, textureInfo );
488 NotifyObservers( textureInfo, true );
492 DALI_LOG_ERROR( "TextureManager::ApplyMask to %s failed\n", textureInfo.url.GetUrl().c_str() );
493 textureInfo.loadState = LOAD_FAILED;
494 NotifyObservers( textureInfo, false );
500 void TextureManager::ApplyMask(
501 Devel::PixelBuffer& pixelBuffer, TextureId maskTextureId,
502 float contentScale, bool cropToMask )
504 int maskCacheIndex = GetCacheIndexFromId( maskTextureId );
505 Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer;
506 pixelBuffer.ApplyMask( maskPixelBuffer, contentScale, cropToMask );
509 void TextureManager::UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo )
511 if( textureInfo.useAtlas != USE_ATLAS )
513 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, " TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId );
515 Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight() );
516 PixelData pixelData = Devel::PixelBuffer::Convert( pixelBuffer );
517 texture.Upload( pixelData );
518 textureInfo.textureSet = TextureSet::New();
519 textureInfo.textureSet.SetTexture( 0u, texture );
522 // Update the load state.
523 // Note: This is regardless of success as we care about whether a
524 // load attempt is in progress or not. If unsuccessful, a broken
525 // image is still loaded.
526 textureInfo.loadState = UPLOADED;
529 void TextureManager::NotifyObservers( TextureInfo& textureInfo, bool success )
531 TextureId textureId = textureInfo.textureId;
533 // If there is an observer: Notify the load is complete, whether successful or not,
534 // and erase it from the list
535 unsigned int observerCount = textureInfo.observerList.Count();
536 TextureInfo* info = &textureInfo;
538 while( observerCount )
540 TextureUploadObserver* observer = info->observerList[0];
542 // During UploadComplete() a Control ResourceReady() signal is emitted.
543 // During that signal the app may add remove /add Textures (e.g. via
544 // ImageViews). At this point no more observers can be added to the
545 // observerList, because textureInfo.loadState = UPLOADED. However it is
546 // possible for observers to be removed, hence we check the observer list
547 // count every iteration.
549 // The reference to the textureInfo struct can also become invalidated,
550 // because new load requests can modify the mTextureInfoContainer list
551 // (e.g. if more requests are pushed back it can cause the list to be
552 // resized invalidating the reference to the TextureInfo ).
553 observer->UploadComplete( success, info->textureId, info->textureSet, info->useAtlas, info->atlasRect );
554 observer->DestructionSignal().Disconnect( this, &TextureManager::ObserverDestroyed );
556 // Get the textureInfo from the container again as it may have been
559 int textureInfoIndex = GetCacheIndexFromId( textureId );
560 if( textureInfoIndex == INVALID_CACHE_INDEX)
562 return; // texture has been removed - can stop.
565 info = &mTextureInfoContainer[ textureInfoIndex ];
566 observerCount = info->observerList.Count();
567 if ( observerCount > 0 )
569 // remove the observer that was just triggered if it's still in the list
570 for( TextureInfo::ObserverListType::Iterator j = info->observerList.Begin(); j != info->observerList.End(); ++j )
574 info->observerList.Erase( j );
583 TextureManager::TextureId TextureManager::GenerateUniqueTextureId()
585 return mCurrentTextureId++;
588 int TextureManager::GetCacheIndexFromId( const TextureId textureId )
590 const unsigned int size = mTextureInfoContainer.size();
592 for( unsigned int i = 0; i < size; ++i )
594 if( mTextureInfoContainer[i].textureId == textureId )
600 DALI_LOG_WARNING( "Cannot locate TextureId: %d\n", textureId );
601 return INVALID_CACHE_INDEX;
604 TextureManager::TextureHash TextureManager::GenerateHash(
605 const std::string& url,
606 const ImageDimensions size,
607 const FittingMode::Type fittingMode,
608 const Dali::SamplingMode::Type samplingMode,
609 const UseAtlas useAtlas,
610 TextureId maskTextureId )
612 std::string hashTarget( url );
613 const size_t urlLength = hashTarget.length();
614 const uint16_t width = size.GetWidth();
615 const uint16_t height = size.GetWidth();
617 // If either the width or height has been specified, include the resizing options in the hash
618 if( width != 0 || height != 0 )
620 // We are appending 5 bytes to the URL to form the hash input.
621 hashTarget.resize( urlLength + 5u );
622 char* hashTargetPtr = &( hashTarget[ urlLength ] );
624 // Pack the width and height (4 bytes total).
625 *hashTargetPtr++ = size.GetWidth() & 0xff;
626 *hashTargetPtr++ = ( size.GetWidth() >> 8u ) & 0xff;
627 *hashTargetPtr++ = size.GetHeight() & 0xff;
628 *hashTargetPtr++ = ( size.GetHeight() >> 8u ) & 0xff;
630 // Bit-pack the FittingMode, SamplingMode and atlasing.
631 // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
632 *hashTargetPtr = ( fittingMode << 4u ) | ( samplingMode << 1 ) | useAtlas;
636 // We are not including sizing information, but we still need an extra byte for atlasing.
637 hashTarget.resize( urlLength + 1u );
638 // Add the atlasing to the hash input.
639 hashTarget[ urlLength ] = useAtlas;
642 if( maskTextureId != INVALID_TEXTURE_ID )
644 hashTarget.resize( urlLength + sizeof( TextureId ) );
645 TextureId* hashTargetPtr = reinterpret_cast<TextureId*>(&( hashTarget[ urlLength ] ));
647 // Append the hash target to the end of the URL byte by byte:
648 // (to avoid SIGBUS / alignment issues)
649 for( size_t byteIter = 0; byteIter < sizeof( TextureId ); ++byteIter )
651 *hashTargetPtr++ = maskTextureId & 0xff;
652 maskTextureId >>= 8u;
656 return Dali::CalculateHash( hashTarget );
659 int TextureManager::FindCachedTexture(
660 const TextureManager::TextureHash hash,
661 const std::string& url,
662 const ImageDimensions size,
663 const FittingMode::Type fittingMode,
664 const Dali::SamplingMode::Type samplingMode,
666 TextureId maskTextureId)
668 // Default to an invalid ID, in case we do not find a match.
669 int cacheIndex = INVALID_CACHE_INDEX;
671 // Iterate through our hashes to find a match.
672 const unsigned int count = mTextureInfoContainer.size();
673 for( unsigned int i = 0u; i < count; ++i )
675 if( mTextureInfoContainer[i].hash == hash )
677 // We have a match, now we check all the original parameters in case of a hash collision.
678 TextureInfo& textureInfo( mTextureInfoContainer[i] );
680 if( ( url == textureInfo.url.GetUrl() ) &&
681 ( useAtlas == textureInfo.useAtlas ) &&
682 ( maskTextureId == textureInfo.maskTextureId ) &&
683 ( size == textureInfo.desiredSize ) &&
684 ( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
685 ( fittingMode == textureInfo.fittingMode &&
686 samplingMode == textureInfo.samplingMode ) ) )
688 // The found Texture is a match.
698 void TextureManager::ObserverDestroyed( TextureUploadObserver* observer )
700 const unsigned int count = mTextureInfoContainer.size();
701 for( unsigned int i = 0; i < count; ++i )
703 TextureInfo& textureInfo( mTextureInfoContainer[i] );
704 for( TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin(); j != textureInfo.observerList.End(); )
708 j = textureInfo.observerList.Erase( j );
718 TextureManager::~TextureManager()
722 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(TextureManager& textureManager)
723 : AsyncLoadingHelper(Toolkit::AsyncImageLoader::New(), textureManager,
724 AsyncLoadingInfoContainerType())
728 void TextureManager::AsyncLoadingHelper::Load(TextureId textureId,
729 const VisualUrl& url,
730 ImageDimensions desiredSize,
731 FittingMode::Type fittingMode,
732 SamplingMode::Type samplingMode,
733 bool orientationCorrection)
735 mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId));
736 auto id = mLoader.Load(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
737 mLoadingInfoContainer.back().loadId = id;
740 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(AsyncLoadingHelper&& rhs)
741 : AsyncLoadingHelper(rhs.mLoader, rhs.mTextureManager, std::move(rhs.mLoadingInfoContainer))
745 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(
746 Toolkit::AsyncImageLoader loader,
747 TextureManager& textureManager,
748 AsyncLoadingInfoContainerType&& loadingInfoContainer)
750 mTextureManager(textureManager),
751 mLoadingInfoContainer(std::move(loadingInfoContainer))
753 DevelAsyncImageLoader::PixelBufferLoadedSignal(mLoader).Connect(
754 this, &AsyncLoadingHelper::AsyncLoadComplete);
757 void TextureManager::AsyncLoadingHelper::AsyncLoadComplete(uint32_t id,
758 Devel::PixelBuffer pixelBuffer)
760 mTextureManager.AsyncLoadComplete(mLoadingInfoContainer, id, pixelBuffer);
763 } // namespace Internal
765 } // namespace Toolkit