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 "texture-manager.h"
23 #include <dali/devel-api/adaptor-framework/environment-variable.h>
24 #include <dali/devel-api/common/hash.h>
25 #include <dali/devel-api/images/texture-set-image.h>
26 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
27 #include <dali/integration-api/debug.h>
30 #include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
31 #include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
36 constexpr auto DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS = size_t{4u};
37 constexpr auto DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS = size_t{8u};
39 constexpr auto NUMBER_OF_LOCAL_LOADER_THREADS_ENV = "DALI_TEXTURE_LOCAL_THREADS";
40 constexpr auto NUMBER_OF_REMOTE_LOADER_THREADS_ENV = "DALI_TEXTURE_REMOTE_THREADS";
42 size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue)
44 using Dali::EnvironmentVariable::GetEnvironmentVariable;
45 auto numberString = GetEnvironmentVariable(environmentVariable);
46 auto numberOfThreads = numberString ? std::strtol(numberString, nullptr, 10) : 0;
47 return (numberOfThreads > 0) ? numberOfThreads : defaultValue;
50 size_t GetNumberOfLocalLoaderThreads()
52 return GetNumberOfThreads(NUMBER_OF_LOCAL_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS);
55 size_t GetNumberOfRemoteLoaderThreads()
57 return GetNumberOfThreads(NUMBER_OF_REMOTE_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS);
75 Debug::Filter* gTextureManagerLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_TEXTURE_MANAGER" );
78 const uint32_t DEFAULT_ATLAS_SIZE( 1024u ); ///< This size can fit 8 by 8 images of average size 128 * 128
79 const Vector4 FULL_ATLAS_RECT( 0.0f, 0.0f, 1.0f, 1.0f ); ///< UV Rectangle that covers the full Texture
80 const char * const BROKEN_IMAGE_URL( DALI_IMAGE_DIR "broken.png" ); ///< URL For the broken image placeholder
81 const int INVALID_INDEX( -1 ); ///< Invalid index used to represent a non-existant TextureInfo struct
82 const int INVALID_CACHE_INDEX( -1 ); ///< Invalid Cache index
84 } // Anonymous namespace
87 TextureManager::TextureManager()
88 : mCurrentTextureId( 0 ),
89 mAsyncLocalLoaders( GetNumberOfLocalLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ),
90 mAsyncRemoteLoaders( GetNumberOfRemoteLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } )
94 TextureManager::TextureId TextureManager::RequestLoad(
96 const ImageDimensions desiredSize,
97 FittingMode::Type fittingMode,
98 Dali::SamplingMode::Type samplingMode,
99 const UseAtlas useAtlas,
100 TextureUploadObserver* observer )
102 return RequestLoadInternal( url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, UPLOAD_TO_TEXTURE, observer );
105 TextureManager::TextureId TextureManager::RequestLoad(
106 const VisualUrl& url,
107 TextureId maskTextureId,
109 const ImageDimensions desiredSize,
110 FittingMode::Type fittingMode,
111 Dali::SamplingMode::Type samplingMode,
112 const UseAtlas useAtlas,
114 TextureUploadObserver* observer )
116 return RequestLoadInternal( url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, UPLOAD_TO_TEXTURE, observer );
119 TextureManager::TextureId TextureManager::RequestMaskLoad( const VisualUrl& maskUrl )
121 // Use the normal load procedure to get the alpha mask.
122 return RequestLoadInternal( maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, KEEP_PIXEL_BUFFER, NULL );
126 TextureManager::TextureId TextureManager::RequestLoadInternal(
127 const VisualUrl& url,
128 TextureId maskTextureId,
130 const ImageDimensions desiredSize,
131 FittingMode::Type fittingMode,
132 Dali::SamplingMode::Type samplingMode,
135 StorageType storageType,
136 TextureUploadObserver* observer )
138 // First check if the requested Texture is cached.
139 const TextureHash textureHash = GenerateHash( url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId );
141 TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
143 // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
144 int cacheIndex = FindCachedTexture( textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId );
146 // Check if the requested Texture exists in the cache.
147 if( cacheIndex != INVALID_CACHE_INDEX )
149 // Mark this texture being used by another client resource.
150 ++( mTextureInfoContainer[ cacheIndex ].referenceCount );
151 textureId = mTextureInfoContainer[ cacheIndex ].textureId;
153 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 );
156 if( textureId == INVALID_TEXTURE_ID ) // There was no caching, or caching not required
158 // We need a new Texture.
159 textureId = GenerateUniqueTextureId();
160 mTextureInfoContainer.push_back( TextureInfo( textureId, maskTextureId, url.GetUrl(),
161 desiredSize, contentScale, fittingMode, samplingMode,
162 false, cropToMask, useAtlas, textureHash ) );
163 cacheIndex = mTextureInfoContainer.size() - 1u;
165 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 );
168 // The below code path is common whether we are using the cache or not.
169 // The textureInfoIndex now refers to either a pre-existing cached TextureInfo,
170 // or a new TextureInfo just created.
171 TextureInfo& textureInfo( mTextureInfoContainer[ cacheIndex ] );
172 textureInfo.maskTextureId = maskTextureId;
173 textureInfo.storageType = storageType;
175 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureInfo loadState:%s\n",
176 textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
177 textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
178 textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
179 textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
181 // 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.
182 switch( textureInfo.loadState )
184 case TextureManager::NOT_STARTED:
186 LoadTexture( textureInfo );
187 ObserveTexture( textureInfo, observer );
190 case TextureManager::LOADING:
192 ObserveTexture( textureInfo, observer );
195 case TextureManager::UPLOADED:
199 // The Texture has already loaded. The other observers have already been notified.
200 // We need to send a "late" loaded notification for this observer.
201 observer->UploadComplete( true, textureInfo.textureId, textureInfo.textureSet,
202 textureInfo.useAtlas, textureInfo.atlasRect );
206 case TextureManager::CANCELLED:
208 // A cancelled texture hasn't finished loading yet. Treat as a loading texture
209 // (it's ref count has already been incremented, above)
210 textureInfo.loadState = TextureManager::LOADING;
211 ObserveTexture( textureInfo, observer );
214 case TextureManager::LOAD_FINISHED:
215 case TextureManager::WAITING_FOR_MASK:
216 case TextureManager::LOAD_FAILED:
217 // Loading has already completed. Do nothing.
221 // Return the TextureId for which this Texture can now be referenced by externally.
225 void TextureManager::Remove( const TextureManager::TextureId textureId )
227 int textureInfoIndex = GetCacheIndexFromId( textureId );
228 if( textureInfoIndex != INVALID_INDEX )
230 TextureInfo& textureInfo( mTextureInfoContainer[ textureInfoIndex ] );
232 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::Remove(%d) cacheIdx:%d loadState:%s\n",
233 textureId, textureInfoIndex,
234 textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
235 textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
236 textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
237 textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
239 // Decrement the reference count and check if this is the last user of this Texture.
240 if( --textureInfo.referenceCount <= 0 )
242 // This is the last remove for this Texture.
243 textureInfo.referenceCount = 0;
244 bool removeTextureInfo = false;
246 // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
247 if( textureInfo.loadState == UPLOADED )
249 if( textureInfo.atlas )
251 textureInfo.atlas.Remove( textureInfo.atlasRect );
253 removeTextureInfo = true;
255 else if( textureInfo.loadState == LOADING )
257 // We mark the textureInfo for removal.
258 // Once the load has completed, this method will be called again.
259 textureInfo.loadState = CANCELLED;
263 // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
264 removeTextureInfo = true;
267 // If the state allows us to remove the TextureInfo data, we do so.
268 if( removeTextureInfo )
270 // Permanently remove the textureInfo struct.
271 mTextureInfoContainer.erase( mTextureInfoContainer.begin() + textureInfoIndex );
277 const VisualUrl& TextureManager::GetVisualUrl( TextureId textureId )
279 int cacheIndex = GetCacheIndexFromId( textureId );
280 DALI_ASSERT_DEBUG( cacheIndex != INVALID_CACHE_INDEX && "TextureId out of range");
282 TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
283 return cachedTextureInfo.url;
286 TextureManager::LoadState TextureManager::GetTextureState( TextureId textureId )
288 LoadState loadState = TextureManager::NOT_STARTED;
290 int cacheIndex = GetCacheIndexFromId( textureId );
291 if( cacheIndex != INVALID_CACHE_INDEX )
293 TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
294 loadState = cachedTextureInfo.loadState;
299 TextureSet TextureManager::GetTextureSet( TextureId textureId )
301 TextureSet textureSet;// empty handle
303 int cacheIndex = GetCacheIndexFromId( textureId );
304 if( cacheIndex != INVALID_CACHE_INDEX )
306 TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
307 textureSet = cachedTextureInfo.textureSet;
312 bool TextureManager::LoadTexture( TextureInfo& textureInfo )
316 if( textureInfo.loadState == NOT_STARTED )
318 textureInfo.loadState = LOADING;
320 if( !textureInfo.loadSynchronously )
322 auto& loadersContainer = textureInfo.url.IsLocal() ? mAsyncLocalLoaders : mAsyncRemoteLoaders;
323 auto loadingHelperIt = loadersContainer.GetNext();
324 DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End());
325 loadingHelperIt->Load(textureInfo.textureId, textureInfo.url,
326 textureInfo.desiredSize, textureInfo.fittingMode,
327 textureInfo.samplingMode, true);
334 void TextureManager::ObserveTexture( TextureInfo& textureInfo,
335 TextureUploadObserver* observer )
339 textureInfo.observerList.PushBack( observer );
340 observer->DestructionSignal().Connect( this, &TextureManager::ObserverDestroyed );
344 void TextureManager::AsyncLoadComplete( AsyncLoadingInfoContainerType& loadingContainer, uint32_t id, Devel::PixelBuffer pixelBuffer )
346 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( id:%d )\n", id );
348 if( loadingContainer.size() >= 1u )
350 AsyncLoadingInfo loadingInfo = loadingContainer.front();
352 if( loadingInfo.loadId == id )
354 int cacheIndex = GetCacheIndexFromId( loadingInfo.textureId );
355 if( cacheIndex != INVALID_CACHE_INDEX )
357 TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
359 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, " CacheIndex:%d LoadState: %d\n", cacheIndex, textureInfo.loadState );
361 if( textureInfo.loadState != CANCELLED )
363 // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified)
364 PostLoad( textureInfo, pixelBuffer );
368 Remove( textureInfo.textureId );
373 loadingContainer.pop_front();
377 void TextureManager::PostLoad( TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer )
379 // Was the load successful?
380 if( pixelBuffer && ( pixelBuffer.GetWidth() != 0 ) && ( pixelBuffer.GetHeight() != 0 ) )
382 // No atlas support for now
383 textureInfo.useAtlas = NO_ATLAS;
385 if( textureInfo.storageType == UPLOAD_TO_TEXTURE )
387 // If there is a mask texture ID associated with this texture, then apply the mask
388 // if it's already loaded. If it hasn't, and the mask is still loading,
389 // wait for the mask to finish loading.
390 if( textureInfo.maskTextureId != INVALID_TEXTURE_ID )
392 LoadState maskLoadState = GetTextureState( textureInfo.maskTextureId );
393 if( maskLoadState == LOADING )
395 textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily
396 textureInfo.loadState = WAITING_FOR_MASK;
398 else if( maskLoadState == LOAD_FINISHED )
400 ApplyMask( pixelBuffer, textureInfo.maskTextureId, textureInfo.scaleFactor, textureInfo.cropToMask );
401 UploadTexture( pixelBuffer, textureInfo );
402 NotifyObservers( textureInfo, true );
407 UploadTexture( pixelBuffer, textureInfo );
408 NotifyObservers( textureInfo, true );
413 textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data
414 textureInfo.loadState = LOAD_FINISHED;
416 // Check if there was another texture waiting for this load to complete
417 // (e.g. if this was an image mask, and its load is on a different thread)
418 CheckForWaitingTexture( textureInfo );
423 DALI_LOG_ERROR( "TextureManager::AsyncImageLoad(%s) failed\n", textureInfo.url.GetUrl().c_str() );
424 // @todo If the load was unsuccessful, upload the broken image.
425 textureInfo.loadState = LOAD_FAILED;
426 CheckForWaitingTexture( textureInfo );
427 NotifyObservers( textureInfo, false );
431 void TextureManager::CheckForWaitingTexture( TextureInfo& maskTextureInfo )
433 // Search the cache, checking if any texture has this texture id as a
435 const unsigned int size = mTextureInfoContainer.size();
437 for( unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex )
439 if( mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
440 mTextureInfoContainer[cacheIndex].loadState == WAITING_FOR_MASK )
442 TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
443 Devel::PixelBuffer pixelBuffer = textureInfo.pixelBuffer;
444 textureInfo.pixelBuffer.Reset();
446 if( maskTextureInfo.loadState == LOAD_FINISHED )
448 ApplyMask( pixelBuffer, maskTextureInfo.textureId, textureInfo.scaleFactor, textureInfo.cropToMask );
449 UploadTexture( pixelBuffer, textureInfo );
450 NotifyObservers( textureInfo, true );
454 DALI_LOG_ERROR( "TextureManager::ApplyMask to %s failed\n", textureInfo.url.GetUrl().c_str() );
455 textureInfo.loadState = LOAD_FAILED;
456 NotifyObservers( textureInfo, false );
462 void TextureManager::ApplyMask(
463 Devel::PixelBuffer& pixelBuffer, TextureId maskTextureId,
464 float contentScale, bool cropToMask )
466 int maskCacheIndex = GetCacheIndexFromId( maskTextureId );
467 Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer;
468 pixelBuffer.ApplyMask( maskPixelBuffer, contentScale, cropToMask );
471 void TextureManager::UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo )
473 if( textureInfo.useAtlas != USE_ATLAS )
475 DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, " TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId );
477 Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight() );
478 PixelData pixelData = Devel::PixelBuffer::Convert( pixelBuffer );
479 texture.Upload( pixelData );
480 textureInfo.textureSet = TextureSet::New();
481 textureInfo.textureSet.SetTexture( 0u, texture );
484 // Update the load state.
485 // Note: This is regardless of success as we care about whether a
486 // load attempt is in progress or not. If unsuccessful, a broken
487 // image is still loaded.
488 textureInfo.loadState = UPLOADED;
491 void TextureManager::NotifyObservers( TextureInfo& textureInfo, bool success )
493 TextureId textureId = textureInfo.textureId;
495 // If there is an observer: Notify the load is complete, whether successful or not,
496 // and erase it from the list
497 unsigned int observerCount = textureInfo.observerList.Count();
498 TextureInfo* info = &textureInfo;
500 while( observerCount )
502 TextureUploadObserver* observer = info->observerList[0];
504 // During UploadComplete() a Control ResourceReady() signal is emitted.
505 // During that signal the app may add remove /add Textures (e.g. via
506 // ImageViews). At this point no more observers can be added to the
507 // observerList, because textureInfo.loadState = UPLOADED. However it is
508 // possible for observers to be removed, hence we check the observer list
509 // count every iteration.
511 // The reference to the textureInfo struct can also become invalidated,
512 // because new load requests can modify the mTextureInfoContainer list
513 // (e.g. if more requests are pushed back it can cause the list to be
514 // resized invalidating the reference to the TextureInfo ).
515 observer->UploadComplete( success, info->textureId, info->textureSet, info->useAtlas, info->atlasRect );
516 observer->DestructionSignal().Disconnect( this, &TextureManager::ObserverDestroyed );
518 // Get the textureInfo from the container again as it may have been
521 int textureInfoIndex = GetCacheIndexFromId( textureId );
522 if( textureInfoIndex == INVALID_CACHE_INDEX)
524 return; // texture has been removed - can stop.
527 info = &mTextureInfoContainer[ textureInfoIndex ];
528 observerCount = info->observerList.Count();
529 if ( observerCount > 0 )
531 // remove the observer that was just triggered if it's still in the list
532 for( TextureInfo::ObserverListType::Iterator j = info->observerList.Begin(); j != info->observerList.End(); ++j )
536 info->observerList.Erase( j );
545 TextureManager::TextureId TextureManager::GenerateUniqueTextureId()
547 return mCurrentTextureId++;
550 int TextureManager::GetCacheIndexFromId( const TextureId textureId )
552 const unsigned int size = mTextureInfoContainer.size();
554 for( unsigned int i = 0; i < size; ++i )
556 if( mTextureInfoContainer[i].textureId == textureId )
562 DALI_LOG_WARNING( "Cannot locate TextureId: %d\n", textureId );
563 return INVALID_CACHE_INDEX;
566 TextureManager::TextureHash TextureManager::GenerateHash(
567 const std::string& url,
568 const ImageDimensions size,
569 const FittingMode::Type fittingMode,
570 const Dali::SamplingMode::Type samplingMode,
571 const UseAtlas useAtlas,
572 TextureId maskTextureId )
574 std::string hashTarget( url );
575 const size_t urlLength = hashTarget.length();
576 const uint16_t width = size.GetWidth();
577 const uint16_t height = size.GetWidth();
579 // If either the width or height has been specified, include the resizing options in the hash
580 if( width != 0 || height != 0 )
582 // We are appending 5 bytes to the URL to form the hash input.
583 hashTarget.resize( urlLength + 5u );
584 char* hashTargetPtr = &( hashTarget[ urlLength ] );
586 // Pack the width and height (4 bytes total).
587 *hashTargetPtr++ = size.GetWidth() & 0xff;
588 *hashTargetPtr++ = ( size.GetWidth() >> 8u ) & 0xff;
589 *hashTargetPtr++ = size.GetHeight() & 0xff;
590 *hashTargetPtr++ = ( size.GetHeight() >> 8u ) & 0xff;
592 // Bit-pack the FittingMode, SamplingMode and atlasing.
593 // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
594 *hashTargetPtr = ( fittingMode << 4u ) | ( samplingMode << 1 ) | useAtlas;
598 // We are not including sizing information, but we still need an extra byte for atlasing.
599 hashTarget.resize( urlLength + 1u );
600 // Add the atlasing to the hash input.
601 hashTarget[ urlLength ] = useAtlas;
604 if( maskTextureId != INVALID_TEXTURE_ID )
606 hashTarget.resize( urlLength + sizeof( TextureId ) );
607 TextureId* hashTargetPtr = reinterpret_cast<TextureId*>(&( hashTarget[ urlLength ] ));
609 // Append the hash target to the end of the URL byte by byte:
610 // (to avoid SIGBUS / alignment issues)
611 for( size_t byteIter = 0; byteIter < sizeof( TextureId ); ++byteIter )
613 *hashTargetPtr++ = maskTextureId & 0xff;
614 maskTextureId >>= 8u;
618 return Dali::CalculateHash( hashTarget );
621 int TextureManager::FindCachedTexture(
622 const TextureManager::TextureHash hash,
623 const std::string& url,
624 const ImageDimensions size,
625 const FittingMode::Type fittingMode,
626 const Dali::SamplingMode::Type samplingMode,
628 TextureId maskTextureId)
630 // Default to an invalid ID, in case we do not find a match.
631 int cacheIndex = INVALID_CACHE_INDEX;
633 // Iterate through our hashes to find a match.
634 const unsigned int count = mTextureInfoContainer.size();
635 for( unsigned int i = 0u; i < count; ++i )
637 if( mTextureInfoContainer[i].hash == hash )
639 // We have a match, now we check all the original parameters in case of a hash collision.
640 TextureInfo& textureInfo( mTextureInfoContainer[i] );
642 if( ( url == textureInfo.url.GetUrl() ) &&
643 ( useAtlas == textureInfo.useAtlas ) &&
644 ( maskTextureId == textureInfo.maskTextureId ) &&
645 ( size == textureInfo.desiredSize ) &&
646 ( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
647 ( fittingMode == textureInfo.fittingMode &&
648 samplingMode == textureInfo.samplingMode ) ) )
650 // The found Texture is a match.
660 void TextureManager::ObserverDestroyed( TextureUploadObserver* observer )
662 const unsigned int count = mTextureInfoContainer.size();
663 for( unsigned int i = 0; i < count; ++i )
665 TextureInfo& textureInfo( mTextureInfoContainer[i] );
666 for( TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin(); j != textureInfo.observerList.End(); )
670 j = textureInfo.observerList.Erase( j );
680 TextureManager::~TextureManager()
684 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(TextureManager& textureManager)
685 : AsyncLoadingHelper(Toolkit::AsyncImageLoader::New(), textureManager,
686 AsyncLoadingInfoContainerType())
690 void TextureManager::AsyncLoadingHelper::Load(TextureId textureId,
691 const VisualUrl& url,
692 ImageDimensions desiredSize,
693 FittingMode::Type fittingMode,
694 SamplingMode::Type samplingMode,
695 bool orientationCorrection)
697 mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId));
698 auto id = mLoader.Load(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
699 mLoadingInfoContainer.back().loadId = id;
702 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(AsyncLoadingHelper&& rhs)
703 : AsyncLoadingHelper(rhs.mLoader, rhs.mTextureManager, std::move(rhs.mLoadingInfoContainer))
707 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(
708 Toolkit::AsyncImageLoader loader,
709 TextureManager& textureManager,
710 AsyncLoadingInfoContainerType&& loadingInfoContainer)
712 mTextureManager(textureManager),
713 mLoadingInfoContainer(std::move(loadingInfoContainer))
715 DevelAsyncImageLoader::PixelBufferLoadedSignal(mLoader).Connect(
716 this, &AsyncLoadingHelper::AsyncLoadComplete);
719 void TextureManager::AsyncLoadingHelper::AsyncLoadComplete(uint32_t id,
720 Devel::PixelBuffer pixelBuffer)
722 mTextureManager.AsyncLoadComplete(mLoadingInfoContainer, id, pixelBuffer);
725 } // namespace Internal
727 } // namespace Toolkit