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/public-api/math/vector4.h>
25 #include <dali/devel-api/adaptor-framework/environment-variable.h>
26 #include <dali/devel-api/adaptor-framework/image-loading.h>
27 #include <dali/devel-api/common/hash.h>
28 #include <dali/devel-api/images/texture-set-image.h>
29 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
30 #include <dali/integration-api/debug.h>
33 #include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
34 #include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
35 #include <dali-toolkit/internal/visuals/image-atlas-manager.h>
40 constexpr auto DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS = size_t{4u};
41 constexpr auto DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS = size_t{8u};
43 constexpr auto NUMBER_OF_LOCAL_LOADER_THREADS_ENV = "DALI_TEXTURE_LOCAL_THREADS";
44 constexpr auto NUMBER_OF_REMOTE_LOADER_THREADS_ENV = "DALI_TEXTURE_REMOTE_THREADS";
46 size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue)
48 using Dali::EnvironmentVariable::GetEnvironmentVariable;
49 auto numberString = GetEnvironmentVariable(environmentVariable);
50 auto numberOfThreads = numberString ? std::strtol(numberString, nullptr, 10) : 0;
51 return (numberOfThreads > 0) ? numberOfThreads : defaultValue;
54 size_t GetNumberOfLocalLoaderThreads()
56 return GetNumberOfThreads(NUMBER_OF_LOCAL_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS);
59 size_t GetNumberOfRemoteLoaderThreads()
61 return GetNumberOfThreads(NUMBER_OF_REMOTE_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS);
79 Debug::Filter* gTextureManagerLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_TEXTURE_MANAGER" );
82 const uint32_t DEFAULT_ATLAS_SIZE( 1024u ); ///< This size can fit 8 by 8 images of average size 128 * 128
83 const Vector4 FULL_ATLAS_RECT( 0.0f, 0.0f, 1.0f, 1.0f ); ///< UV Rectangle that covers the full Texture
84 const char * const BROKEN_IMAGE_URL( DALI_IMAGE_DIR "broken.png" ); ///< URL For the broken image placeholder
85 const int INVALID_INDEX( -1 ); ///< Invalid index used to represent a non-existant TextureInfo struct
86 const int INVALID_CACHE_INDEX( -1 ); ///< Invalid Cache index
88 } // Anonymous namespace
90 TextureManager::MaskingData::MaskingData()
92 mAlphaMaskId( INVALID_TEXTURE_ID ),
93 mContentScaleFactor( 1.0f ),
98 TextureManager::TextureManager()
99 : mAsyncLocalLoaders( GetNumberOfLocalLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ),
100 mAsyncRemoteLoaders( GetNumberOfRemoteLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ),
101 mCurrentTextureId( 0 )
105 TextureSet TextureManager::LoadTexture(
106 VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode,
107 Dali::SamplingMode::Type samplingMode, const MaskingDataPointer& maskInfo,
108 bool synchronousLoading, TextureManager::TextureId& textureId, Vector4& textureRect,
109 bool& atlasingStatus, bool& loadingStatus, Dali::WrapMode::Type wrapModeU,
110 Dali::WrapMode::Type wrapModeV, TextureUploadObserver* textureObserver,
111 AtlasUploadObserver* atlasObserver, ImageAtlasManagerPtr imageAtlasManager)
113 TextureSet textureSet;
115 loadingStatus = false;
116 textureRect = FULL_ATLAS_RECT;
118 if( VisualUrl::TEXTURE == url.GetProtocolType())
120 std::string location = url.GetLocation();
121 if( location.size() > 0u )
123 TextureId id = std::stoi( location );
124 for( auto&& elem : mExternalTextures )
126 if( elem.textureId == id )
128 return elem.textureSet;
133 else if( synchronousLoading )
138 Devel::PixelBuffer pixelBuffer = LoadImageFromFile( url.GetUrl(), desiredSize, fittingMode, samplingMode );
141 data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
147 textureSet = TextureSet::New();
148 Devel::PixelBuffer pixelBuffer = LoadImageFromFile( BROKEN_IMAGE_URL );
151 data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
153 Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(),
154 data.GetWidth(), data.GetHeight() );
155 texture.Upload( data );
156 textureSet = TextureSet::New();
157 textureSet.SetTexture( 0u, texture );
161 if( atlasingStatus ) // attempt atlasing
163 textureSet = imageAtlasManager->Add( textureRect, data );
165 if( !textureSet ) // big image, no atlasing or atlasing failed
167 atlasingStatus = false;
168 Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(),
169 data.GetWidth(), data.GetHeight() );
170 texture.Upload( data );
171 textureSet = TextureSet::New();
172 textureSet.SetTexture( 0u, texture );
178 loadingStatus = true;
181 textureSet = imageAtlasManager->Add( textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver );
183 if( !textureSet ) // big image, no atlasing or atlasing failed
185 atlasingStatus = false;
188 textureId = RequestLoad( url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, textureObserver );
192 textureId = RequestLoad( url,
193 maskInfo->mAlphaMaskId,
194 maskInfo->mContentScaleFactor,
196 fittingMode, samplingMode,
197 TextureManager::NO_ATLAS,
198 maskInfo->mCropToMask,
202 TextureManager::LoadState loadState = GetTextureState( textureId );
203 loadingStatus = ( loadState == TextureManager::LOADING );
205 if( loadState == TextureManager::UPLOADED )
207 // UploadComplete has already been called - keep the same texture set
208 textureSet = GetTextureSet( textureId );
213 if( ! atlasingStatus && textureSet )
215 Sampler sampler = Sampler::New();
216 sampler.SetWrapMode( wrapModeU, wrapModeV );
217 textureSet.SetSampler( 0u, sampler );
223 TextureManager::TextureId TextureManager::RequestLoad(
224 const VisualUrl& url,
225 const ImageDimensions desiredSize,
226 FittingMode::Type fittingMode,
227 Dali::SamplingMode::Type samplingMode,
228 const UseAtlas useAtlas,
229 TextureUploadObserver* observer )
231 return RequestLoadInternal( url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, UPLOAD_TO_TEXTURE, observer );
234 TextureManager::TextureId TextureManager::RequestLoad(
235 const VisualUrl& url,
236 TextureId maskTextureId,
238 const ImageDimensions desiredSize,
239 FittingMode::Type fittingMode,
240 Dali::SamplingMode::Type samplingMode,
241 const UseAtlas useAtlas,
243 TextureUploadObserver* observer )
245 return RequestLoadInternal( url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, UPLOAD_TO_TEXTURE, observer );
248 TextureManager::TextureId TextureManager::RequestMaskLoad( const VisualUrl& maskUrl )
250 // Use the normal load procedure to get the alpha mask.
251 return RequestLoadInternal( maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, KEEP_PIXEL_BUFFER, NULL );
254 TextureManager::TextureId TextureManager::RequestLoadInternal(
255 const VisualUrl& url,
256 TextureId maskTextureId,
258 const ImageDimensions desiredSize,
259 FittingMode::Type fittingMode,
260 Dali::SamplingMode::Type samplingMode,
263 StorageType storageType,
264 TextureUploadObserver* observer )
266 // First check if the requested Texture is cached.
267 const TextureHash textureHash = GenerateHash( url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId );
269 TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
271 // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
272 int cacheIndex = FindCachedTexture( textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId );
274 // Check if the requested Texture exists in the cache.
275 if( cacheIndex != INVALID_CACHE_INDEX )
277 // Mark this texture being used by another client resource.
278 ++( mTextureInfoContainer[ cacheIndex ].referenceCount );
279 textureId = mTextureInfoContainer[ cacheIndex ].textureId;
281 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 );
284 if( textureId == INVALID_TEXTURE_ID ) // There was no caching, or caching not required
286 // We need a new Texture.
287 textureId = GenerateUniqueTextureId();
288 mTextureInfoContainer.push_back( TextureInfo( textureId, maskTextureId, url.GetUrl(),
289 desiredSize, contentScale, fittingMode, samplingMode,
290 false, cropToMask, useAtlas, textureHash ) );
291 cacheIndex = mTextureInfoContainer.size() - 1u;
293 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 );
296 // The below code path is common whether we are using the cache or not.
297 // The textureInfoIndex now refers to either a pre-existing cached TextureInfo,
298 // or a new TextureInfo just created.
299 TextureInfo& textureInfo( mTextureInfoContainer[ cacheIndex ] );
300 textureInfo.maskTextureId = maskTextureId;
301 textureInfo.storageType = storageType;
303 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureInfo loadState:%s\n",
304 textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
305 textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
306 textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
307 textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
309 // 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.
310 switch( textureInfo.loadState )
312 case TextureManager::NOT_STARTED:
314 LoadTexture( textureInfo );
315 ObserveTexture( textureInfo, observer );
318 case TextureManager::LOADING:
320 ObserveTexture( textureInfo, observer );
323 case TextureManager::UPLOADED:
327 // The Texture has already loaded. The other observers have already been notified.
328 // We need to send a "late" loaded notification for this observer.
329 observer->UploadComplete( true, textureInfo.textureId, textureInfo.textureSet,
330 textureInfo.useAtlas, textureInfo.atlasRect );
334 case TextureManager::CANCELLED:
336 // A cancelled texture hasn't finished loading yet. Treat as a loading texture
337 // (it's ref count has already been incremented, above)
338 textureInfo.loadState = TextureManager::LOADING;
339 ObserveTexture( textureInfo, observer );
342 case TextureManager::LOAD_FINISHED:
343 case TextureManager::WAITING_FOR_MASK:
344 case TextureManager::LOAD_FAILED:
345 // Loading has already completed. Do nothing.
349 // Return the TextureId for which this Texture can now be referenced by externally.
353 void TextureManager::Remove( const TextureManager::TextureId textureId )
355 int textureInfoIndex = GetCacheIndexFromId( textureId );
356 if( textureInfoIndex != INVALID_INDEX )
358 TextureInfo& textureInfo( mTextureInfoContainer[ textureInfoIndex ] );
360 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::Remove(%d) cacheIdx:%d loadState:%s\n",
361 textureId, textureInfoIndex,
362 textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
363 textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
364 textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
365 textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
367 // Decrement the reference count and check if this is the last user of this Texture.
368 if( --textureInfo.referenceCount <= 0 )
370 // This is the last remove for this Texture.
371 textureInfo.referenceCount = 0;
372 bool removeTextureInfo = false;
374 // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
375 if( textureInfo.loadState == UPLOADED )
377 if( textureInfo.atlas )
379 textureInfo.atlas.Remove( textureInfo.atlasRect );
381 removeTextureInfo = true;
383 else if( textureInfo.loadState == LOADING )
385 // We mark the textureInfo for removal.
386 // Once the load has completed, this method will be called again.
387 textureInfo.loadState = CANCELLED;
391 // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
392 removeTextureInfo = true;
395 // If the state allows us to remove the TextureInfo data, we do so.
396 if( removeTextureInfo )
398 // Permanently remove the textureInfo struct.
399 mTextureInfoContainer.erase( mTextureInfoContainer.begin() + textureInfoIndex );
405 const VisualUrl& TextureManager::GetVisualUrl( TextureId textureId )
407 int cacheIndex = GetCacheIndexFromId( textureId );
408 DALI_ASSERT_DEBUG( cacheIndex != INVALID_CACHE_INDEX && "TextureId out of range");
410 TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
411 return cachedTextureInfo.url;
414 TextureManager::LoadState TextureManager::GetTextureState( TextureId textureId )
416 LoadState loadState = TextureManager::NOT_STARTED;
418 int cacheIndex = GetCacheIndexFromId( textureId );
419 if( cacheIndex != INVALID_CACHE_INDEX )
421 TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
422 loadState = cachedTextureInfo.loadState;
427 TextureSet TextureManager::GetTextureSet( TextureId textureId )
429 TextureSet textureSet;// empty handle
431 int cacheIndex = GetCacheIndexFromId( textureId );
432 if( cacheIndex != INVALID_CACHE_INDEX )
434 TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
435 textureSet = cachedTextureInfo.textureSet;
440 std::string TextureManager::AddExternalTexture( TextureSet& textureSet )
442 TextureManager::ExternalTextureInfo info;
443 info.textureId = GenerateUniqueTextureId();
444 info.textureSet = textureSet;
445 mExternalTextures.emplace_back( info );
446 return VisualUrl::CreateTextureUrl( std::to_string( info.textureId ) );
449 TextureSet TextureManager::RemoveExternalTexture( const std::string& url )
451 if( url.size() > 0u )
453 // get the location from the Url
454 VisualUrl parseUrl( url );
455 if( VisualUrl::TEXTURE == parseUrl.GetProtocolType() )
457 std::string location = parseUrl.GetLocation();
458 if( location.size() > 0u )
460 TextureId id = std::stoi( location );
461 const auto end = mExternalTextures.end();
462 for( auto iter = mExternalTextures.begin(); iter != end; ++iter )
464 if( iter->textureId == id )
466 auto textureSet = iter->textureSet;
467 mExternalTextures.erase( iter );
477 bool TextureManager::LoadTexture( TextureInfo& textureInfo )
481 if( textureInfo.loadState == NOT_STARTED )
483 textureInfo.loadState = LOADING;
485 if( !textureInfo.loadSynchronously )
487 auto& loadersContainer = textureInfo.url.IsLocalResource() ? mAsyncLocalLoaders : mAsyncRemoteLoaders;
488 auto loadingHelperIt = loadersContainer.GetNext();
489 DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End());
490 loadingHelperIt->Load(textureInfo.textureId, textureInfo.url,
491 textureInfo.desiredSize, textureInfo.fittingMode,
492 textureInfo.samplingMode, true);
499 void TextureManager::ObserveTexture( TextureInfo& textureInfo,
500 TextureUploadObserver* observer )
504 textureInfo.observerList.PushBack( observer );
505 observer->DestructionSignal().Connect( this, &TextureManager::ObserverDestroyed );
509 void TextureManager::AsyncLoadComplete( AsyncLoadingInfoContainerType& loadingContainer, uint32_t id, Devel::PixelBuffer pixelBuffer )
511 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( id:%d )\n", id );
513 if( loadingContainer.size() >= 1u )
515 AsyncLoadingInfo loadingInfo = loadingContainer.front();
517 if( loadingInfo.loadId == id )
519 int cacheIndex = GetCacheIndexFromId( loadingInfo.textureId );
520 if( cacheIndex != INVALID_CACHE_INDEX )
522 TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
524 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, " CacheIndex:%d LoadState: %d\n", cacheIndex, textureInfo.loadState );
526 if( textureInfo.loadState != CANCELLED )
528 // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified)
529 PostLoad( textureInfo, pixelBuffer );
533 Remove( textureInfo.textureId );
538 loadingContainer.pop_front();
542 void TextureManager::PostLoad( TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer )
544 // Was the load successful?
545 if( pixelBuffer && ( pixelBuffer.GetWidth() != 0 ) && ( pixelBuffer.GetHeight() != 0 ) )
547 // No atlas support for now
548 textureInfo.useAtlas = NO_ATLAS;
550 if( textureInfo.storageType == UPLOAD_TO_TEXTURE )
552 // If there is a mask texture ID associated with this texture, then apply the mask
553 // if it's already loaded. If it hasn't, and the mask is still loading,
554 // wait for the mask to finish loading.
555 if( textureInfo.maskTextureId != INVALID_TEXTURE_ID )
557 LoadState maskLoadState = GetTextureState( textureInfo.maskTextureId );
558 if( maskLoadState == LOADING )
560 textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily
561 textureInfo.loadState = WAITING_FOR_MASK;
563 else if( maskLoadState == LOAD_FINISHED )
565 ApplyMask( pixelBuffer, textureInfo.maskTextureId, textureInfo.scaleFactor, textureInfo.cropToMask );
566 UploadTexture( pixelBuffer, textureInfo );
567 NotifyObservers( textureInfo, true );
572 UploadTexture( pixelBuffer, textureInfo );
573 NotifyObservers( textureInfo, true );
578 textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data
579 textureInfo.loadState = LOAD_FINISHED;
581 // Check if there was another texture waiting for this load to complete
582 // (e.g. if this was an image mask, and its load is on a different thread)
583 CheckForWaitingTexture( textureInfo );
588 DALI_LOG_ERROR( "TextureManager::AsyncImageLoad(%s) failed\n", textureInfo.url.GetUrl().c_str() );
589 // @todo If the load was unsuccessful, upload the broken image.
590 textureInfo.loadState = LOAD_FAILED;
591 CheckForWaitingTexture( textureInfo );
592 NotifyObservers( textureInfo, false );
596 void TextureManager::CheckForWaitingTexture( TextureInfo& maskTextureInfo )
598 // Search the cache, checking if any texture has this texture id as a
600 const unsigned int size = mTextureInfoContainer.size();
602 for( unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex )
604 if( mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
605 mTextureInfoContainer[cacheIndex].loadState == WAITING_FOR_MASK )
607 TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
608 Devel::PixelBuffer pixelBuffer = textureInfo.pixelBuffer;
609 textureInfo.pixelBuffer.Reset();
611 if( maskTextureInfo.loadState == LOAD_FINISHED )
613 ApplyMask( pixelBuffer, maskTextureInfo.textureId, textureInfo.scaleFactor, textureInfo.cropToMask );
614 UploadTexture( pixelBuffer, textureInfo );
615 NotifyObservers( textureInfo, true );
619 DALI_LOG_ERROR( "TextureManager::ApplyMask to %s failed\n", textureInfo.url.GetUrl().c_str() );
620 textureInfo.loadState = LOAD_FAILED;
621 NotifyObservers( textureInfo, false );
627 void TextureManager::ApplyMask(
628 Devel::PixelBuffer& pixelBuffer, TextureId maskTextureId,
629 float contentScale, bool cropToMask )
631 int maskCacheIndex = GetCacheIndexFromId( maskTextureId );
632 Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer;
633 pixelBuffer.ApplyMask( maskPixelBuffer, contentScale, cropToMask );
636 void TextureManager::UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo )
638 if( textureInfo.useAtlas != USE_ATLAS )
640 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, " TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId );
642 Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight() );
643 PixelData pixelData = Devel::PixelBuffer::Convert( pixelBuffer );
644 texture.Upload( pixelData );
645 textureInfo.textureSet = TextureSet::New();
646 textureInfo.textureSet.SetTexture( 0u, texture );
649 // Update the load state.
650 // Note: This is regardless of success as we care about whether a
651 // load attempt is in progress or not. If unsuccessful, a broken
652 // image is still loaded.
653 textureInfo.loadState = UPLOADED;
656 void TextureManager::NotifyObservers( TextureInfo& textureInfo, bool success )
658 TextureId textureId = textureInfo.textureId;
660 // If there is an observer: Notify the load is complete, whether successful or not,
661 // and erase it from the list
662 unsigned int observerCount = textureInfo.observerList.Count();
663 TextureInfo* info = &textureInfo;
665 while( observerCount )
667 TextureUploadObserver* observer = info->observerList[0];
669 // During UploadComplete() a Control ResourceReady() signal is emitted.
670 // During that signal the app may add remove /add Textures (e.g. via
671 // ImageViews). At this point no more observers can be added to the
672 // observerList, because textureInfo.loadState = UPLOADED. However it is
673 // possible for observers to be removed, hence we check the observer list
674 // count every iteration.
676 // The reference to the textureInfo struct can also become invalidated,
677 // because new load requests can modify the mTextureInfoContainer list
678 // (e.g. if more requests are pushed back it can cause the list to be
679 // resized invalidating the reference to the TextureInfo ).
680 observer->UploadComplete( success, info->textureId, info->textureSet, info->useAtlas, info->atlasRect );
681 observer->DestructionSignal().Disconnect( this, &TextureManager::ObserverDestroyed );
683 // Get the textureInfo from the container again as it may have been
686 int textureInfoIndex = GetCacheIndexFromId( textureId );
687 if( textureInfoIndex == INVALID_CACHE_INDEX)
689 return; // texture has been removed - can stop.
692 info = &mTextureInfoContainer[ textureInfoIndex ];
693 observerCount = info->observerList.Count();
694 if ( observerCount > 0 )
696 // remove the observer that was just triggered if it's still in the list
697 for( TextureInfo::ObserverListType::Iterator j = info->observerList.Begin(); j != info->observerList.End(); ++j )
701 info->observerList.Erase( j );
710 TextureManager::TextureId TextureManager::GenerateUniqueTextureId()
712 return mCurrentTextureId++;
715 int TextureManager::GetCacheIndexFromId( const TextureId textureId )
717 const unsigned int size = mTextureInfoContainer.size();
719 for( unsigned int i = 0; i < size; ++i )
721 if( mTextureInfoContainer[i].textureId == textureId )
727 DALI_LOG_WARNING( "Cannot locate TextureId: %d\n", textureId );
728 return INVALID_CACHE_INDEX;
731 TextureManager::TextureHash TextureManager::GenerateHash(
732 const std::string& url,
733 const ImageDimensions size,
734 const FittingMode::Type fittingMode,
735 const Dali::SamplingMode::Type samplingMode,
736 const UseAtlas useAtlas,
737 TextureId maskTextureId )
739 std::string hashTarget( url );
740 const size_t urlLength = hashTarget.length();
741 const uint16_t width = size.GetWidth();
742 const uint16_t height = size.GetWidth();
744 // If either the width or height has been specified, include the resizing options in the hash
745 if( width != 0 || height != 0 )
747 // We are appending 5 bytes to the URL to form the hash input.
748 hashTarget.resize( urlLength + 5u );
749 char* hashTargetPtr = &( hashTarget[ urlLength ] );
751 // Pack the width and height (4 bytes total).
752 *hashTargetPtr++ = size.GetWidth() & 0xff;
753 *hashTargetPtr++ = ( size.GetWidth() >> 8u ) & 0xff;
754 *hashTargetPtr++ = size.GetHeight() & 0xff;
755 *hashTargetPtr++ = ( size.GetHeight() >> 8u ) & 0xff;
757 // Bit-pack the FittingMode, SamplingMode and atlasing.
758 // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
759 *hashTargetPtr = ( fittingMode << 4u ) | ( samplingMode << 1 ) | useAtlas;
763 // We are not including sizing information, but we still need an extra byte for atlasing.
764 hashTarget.resize( urlLength + 1u );
765 // Add the atlasing to the hash input.
766 hashTarget[ urlLength ] = useAtlas;
769 if( maskTextureId != INVALID_TEXTURE_ID )
771 hashTarget.resize( urlLength + sizeof( TextureId ) );
772 TextureId* hashTargetPtr = reinterpret_cast<TextureId*>(&( hashTarget[ urlLength ] ));
774 // Append the hash target to the end of the URL byte by byte:
775 // (to avoid SIGBUS / alignment issues)
776 for( size_t byteIter = 0; byteIter < sizeof( TextureId ); ++byteIter )
778 *hashTargetPtr++ = maskTextureId & 0xff;
779 maskTextureId >>= 8u;
783 return Dali::CalculateHash( hashTarget );
786 int TextureManager::FindCachedTexture(
787 const TextureManager::TextureHash hash,
788 const std::string& url,
789 const ImageDimensions size,
790 const FittingMode::Type fittingMode,
791 const Dali::SamplingMode::Type samplingMode,
793 TextureId maskTextureId)
795 // Default to an invalid ID, in case we do not find a match.
796 int cacheIndex = INVALID_CACHE_INDEX;
798 // Iterate through our hashes to find a match.
799 const unsigned int count = mTextureInfoContainer.size();
800 for( unsigned int i = 0u; i < count; ++i )
802 if( mTextureInfoContainer[i].hash == hash )
804 // We have a match, now we check all the original parameters in case of a hash collision.
805 TextureInfo& textureInfo( mTextureInfoContainer[i] );
807 if( ( url == textureInfo.url.GetUrl() ) &&
808 ( useAtlas == textureInfo.useAtlas ) &&
809 ( maskTextureId == textureInfo.maskTextureId ) &&
810 ( size == textureInfo.desiredSize ) &&
811 ( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
812 ( fittingMode == textureInfo.fittingMode &&
813 samplingMode == textureInfo.samplingMode ) ) )
815 // The found Texture is a match.
825 void TextureManager::ObserverDestroyed( TextureUploadObserver* observer )
827 const unsigned int count = mTextureInfoContainer.size();
828 for( unsigned int i = 0; i < count; ++i )
830 TextureInfo& textureInfo( mTextureInfoContainer[i] );
831 for( TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin(); j != textureInfo.observerList.End(); )
835 j = textureInfo.observerList.Erase( j );
845 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(TextureManager& textureManager)
846 : AsyncLoadingHelper(Toolkit::AsyncImageLoader::New(), textureManager,
847 AsyncLoadingInfoContainerType())
851 void TextureManager::AsyncLoadingHelper::Load(TextureId textureId,
852 const VisualUrl& url,
853 ImageDimensions desiredSize,
854 FittingMode::Type fittingMode,
855 SamplingMode::Type samplingMode,
856 bool orientationCorrection)
858 mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId));
859 auto id = mLoader.Load(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
860 mLoadingInfoContainer.back().loadId = id;
863 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(AsyncLoadingHelper&& rhs)
864 : AsyncLoadingHelper(rhs.mLoader, rhs.mTextureManager, std::move(rhs.mLoadingInfoContainer))
868 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(
869 Toolkit::AsyncImageLoader loader,
870 TextureManager& textureManager,
871 AsyncLoadingInfoContainerType&& loadingInfoContainer)
873 mTextureManager(textureManager),
874 mLoadingInfoContainer(std::move(loadingInfoContainer))
876 DevelAsyncImageLoader::PixelBufferLoadedSignal(mLoader).Connect(
877 this, &AsyncLoadingHelper::AsyncLoadComplete);
880 void TextureManager::AsyncLoadingHelper::AsyncLoadComplete(uint32_t id,
881 Devel::PixelBuffer pixelBuffer)
883 mTextureManager.AsyncLoadComplete(mLoadingInfoContainer, id, pixelBuffer);
886 } // namespace Internal
888 } // namespace Toolkit