Merge "Check character glyph index before using cached Font" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / texture-manager.cpp
1  /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include "texture-manager.h"
20
21 // EXTERNAL HEADERS
22 #include <dali/devel-api/common/hash.h>
23 #include <dali/devel-api/images/pixel-data-mask.h>
24 #include <dali/devel-api/images/texture-set-image.h>
25 #include <dali/integration-api/debug.h>
26
27 // INTERNAL HEADERS
28 #include <dali/integration-api/debug.h>
29 #include <dali-toolkit/internal/image-loader/async-image-loader-impl.h>
30 #include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
31 #include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
32
33
34 namespace Dali
35 {
36
37 namespace Toolkit
38 {
39
40 namespace Internal
41 {
42
43 namespace
44 {
45
46 #ifdef DEBUG_ENABLED
47 Debug::Filter* gTextureManagerLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_TEXTURE_MANAGER" );
48 #endif
49
50 const uint32_t      DEFAULT_ATLAS_SIZE( 1024u );                     ///< This size can fit 8 by 8 images of average size 128 * 128
51 const Vector4       FULL_ATLAS_RECT( 0.0f, 0.0f, 1.0f, 1.0f );       ///< UV Rectangle that covers the full Texture
52 const char * const  BROKEN_IMAGE_URL( DALI_IMAGE_DIR "broken.png" ); ///< URL For the broken image placeholder
53 const int           INVALID_INDEX( -1 );                             ///< Invalid index used to represent a non-existant TextureInfo struct
54 const int           INVALID_CACHE_INDEX( -1 ); ///< Invalid Cache index
55
56 } // Anonymous namespace
57
58
59 TextureManager::TextureManager()
60 : mAsyncLocalLoader( Toolkit::AsyncImageLoader::New() ),
61   mAsyncRemoteLoader( Toolkit::AsyncImageLoader::New() ),
62   mCurrentTextureId( 0 )
63 {
64   mAsyncLocalLoader.ImageLoadedSignal().Connect( this, &TextureManager::AsyncLocalLoadComplete );
65   mAsyncRemoteLoader.ImageLoadedSignal().Connect( this, &TextureManager::AsyncRemoteLoadComplete );
66 }
67
68 TextureManager::TextureId TextureManager::RequestLoad(
69   const VisualUrl&         url,
70   const ImageDimensions    desiredSize,
71   FittingMode::Type        fittingMode,
72   Dali::SamplingMode::Type samplingMode,
73   const UseAtlas           useAtlas,
74   TextureUploadObserver*   observer )
75 {
76   return RequestInternalLoad( url, INVALID_TEXTURE_ID, desiredSize, fittingMode, samplingMode, useAtlas, GPU_UPLOAD, observer );
77 }
78
79 TextureManager::TextureId TextureManager::RequestLoad(
80   const VisualUrl&         url,
81   TextureId                maskTextureId,
82   const ImageDimensions    desiredSize,
83   FittingMode::Type        fittingMode,
84   Dali::SamplingMode::Type samplingMode,
85   const UseAtlas           useAtlas,
86   TextureUploadObserver*   observer )
87 {
88   return RequestInternalLoad( url, maskTextureId, desiredSize, fittingMode, samplingMode, useAtlas, GPU_UPLOAD, observer );
89 }
90
91 TextureManager::TextureId TextureManager::RequestMaskLoad( const VisualUrl& maskUrl )
92 {
93   // Use the normal load procedure to get the alpha mask.
94   return RequestInternalLoad( maskUrl, INVALID_TEXTURE_ID, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, CPU, NULL );
95 }
96
97
98 TextureManager::TextureId TextureManager::RequestInternalLoad(
99   const VisualUrl&         url,
100   TextureId                maskTextureId,
101   const ImageDimensions    desiredSize,
102   FittingMode::Type        fittingMode,
103   Dali::SamplingMode::Type samplingMode,
104   UseAtlas                 useAtlas,
105   StorageType              storageType,
106   TextureUploadObserver*   observer )
107 {
108   // First check if the requested Texture is cached.
109   const TextureHash textureHash = GenerateHash( url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId );
110
111   TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
112
113   // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
114   int cacheIndex = FindCachedTexture( textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId );
115
116   // Check if the requested Texture exists in the cache.
117   if( cacheIndex != INVALID_CACHE_INDEX )
118   {
119     // Mark this texture being used by another client resource.
120     ++( mTextureInfoContainer[ cacheIndex ].referenceCount );
121     textureId = mTextureInfoContainer[ cacheIndex ].textureId;
122
123     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 );
124   }
125
126   if( textureId == INVALID_TEXTURE_ID ) // There was no caching, or caching not required
127   {
128     // We need a new Texture.
129     textureId = GenerateUniqueTextureId();
130     mTextureInfoContainer.push_back( TextureInfo( textureId, maskTextureId, url.GetUrl(),
131                                                   desiredSize, fittingMode, samplingMode,
132                                                   false, useAtlas, textureHash ) );
133     cacheIndex = mTextureInfoContainer.size() - 1u;
134
135     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 );
136   }
137
138   // The below code path is common whether we are using the cache or not.
139   // The textureInfoIndex now refers to either a pre-existing cached TextureInfo,
140   // or a new TextureInfo just created.
141   TextureInfo& textureInfo( mTextureInfoContainer[ cacheIndex ] );
142   textureInfo.maskTextureId = maskTextureId;
143   textureInfo.storageType = storageType;
144
145   DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureInfo loadState:%s\n",
146                  textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
147                  textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
148                  textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
149                  textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
150
151   // 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.
152   switch( textureInfo.loadState )
153   {
154     case TextureManager::NOT_STARTED:
155     {
156       LoadTexture( textureInfo );
157       ObserveTexture( textureInfo, observer );
158       break;
159     }
160     case TextureManager::LOADING:
161     {
162       ObserveTexture( textureInfo, observer );
163       break;
164     }
165     case TextureManager::UPLOADED:
166     {
167       if( observer )
168       {
169         // The Texture has already loaded. The other observers have already been notified.
170         // We need to send a "late" loaded notification for this observer.
171         observer->UploadComplete( true,
172                                   textureInfo.textureSet, textureInfo.useAtlas,
173                                   textureInfo.atlasRect );
174       }
175       break;
176     }
177     case TextureManager::CANCELLED:
178     {
179       // A cancelled texture hasn't finished loading yet. Treat as a loading texture
180       // (it's ref count has already been incremented, above)
181       textureInfo.loadState = TextureManager::LOADING;
182       ObserveTexture( textureInfo, observer );
183       break;
184     }
185     case TextureManager::LOAD_FINISHED:
186     case TextureManager::WAITING_FOR_MASK:
187     case TextureManager::LOAD_FAILED:
188       // Loading has already completed. Do nothing.
189       break;
190   }
191
192   // Return the TextureId for which this Texture can now be referenced by externally.
193   return textureId;
194 }
195
196 void TextureManager::Remove( const TextureManager::TextureId textureId )
197 {
198   int textureInfoIndex = GetCacheIndexFromId( textureId );
199   if( textureInfoIndex != INVALID_INDEX )
200   {
201     TextureInfo& textureInfo( mTextureInfoContainer[ textureInfoIndex ] );
202
203
204     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::Remove(%d) cacheIdx:%d loadState:%s\n",
205                    textureId, textureInfoIndex,
206                    textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
207                    textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
208                    textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
209                    textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
210
211     // Decrement the reference count and check if this is the last user of this Texture.
212     if( --textureInfo.referenceCount <= 0 )
213     {
214       // This is the last remove for this Texture.
215       textureInfo.referenceCount = 0;
216       bool removeTextureInfo = false;
217
218       // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
219       if( textureInfo.loadState == UPLOADED )
220       {
221         if( textureInfo.atlas )
222         {
223           textureInfo.atlas.Remove( textureInfo.atlasRect );
224         }
225         removeTextureInfo = true;
226       }
227       else if( textureInfo.loadState == LOADING )
228       {
229         // We mark the textureInfo for removal.
230         // Once the load has completed, this method will be called again.
231         textureInfo.loadState = CANCELLED;
232       }
233       else
234       {
235         // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
236         removeTextureInfo = true;
237       }
238
239       // If the state allows us to remove the TextureInfo data, we do so.
240       if( removeTextureInfo )
241       {
242         // Permanently remove the textureInfo struct.
243         mTextureInfoContainer.erase( mTextureInfoContainer.begin() + textureInfoIndex );
244       }
245     }
246   }
247 }
248
249 TextureManager::LoadState TextureManager::GetTextureState( TextureId textureId )
250 {
251   LoadState loadState = TextureManager::NOT_STARTED;
252
253   int cacheIndex = GetCacheIndexFromId( textureId );
254   if( cacheIndex != INVALID_CACHE_INDEX )
255   {
256     TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
257     loadState = cachedTextureInfo.loadState;
258   }
259   return loadState;
260 }
261
262 TextureSet TextureManager::GetTextureSet( TextureId textureId )
263 {
264   TextureSet textureSet;// empty handle
265
266   int cacheIndex = GetCacheIndexFromId( textureId );
267   if( cacheIndex != INVALID_CACHE_INDEX )
268   {
269     TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
270     textureSet = cachedTextureInfo.textureSet;
271   }
272   return textureSet;
273 }
274
275 bool TextureManager::LoadTexture( TextureInfo& textureInfo )
276 {
277   bool success = true;
278
279   if( textureInfo.loadState == NOT_STARTED )
280   {
281     textureInfo.loadState = LOADING;
282
283     if( !textureInfo.loadSynchronously )
284     {
285       if( textureInfo.url.IsLocal() )
286       {
287         mAsyncLocalLoadingInfoContainer.push_back( AsyncLoadingInfo( textureInfo.textureId ) );
288         mAsyncLocalLoadingInfoContainer.back().loadId =
289           GetImplementation(mAsyncLocalLoader).Load( textureInfo.url, textureInfo.desiredSize,
290                                                      textureInfo.fittingMode,
291                                                      textureInfo.samplingMode, true );
292       }
293       else
294       {
295         mAsyncRemoteLoadingInfoContainer.push_back( AsyncLoadingInfo( textureInfo.textureId ) );
296         mAsyncRemoteLoadingInfoContainer.back().loadId =
297           GetImplementation(mAsyncRemoteLoader).Load( textureInfo.url, textureInfo.desiredSize,
298                                                       textureInfo.fittingMode,
299                                                       textureInfo.samplingMode, true );
300       }
301     }
302   }
303
304   return success;
305 }
306
307 void TextureManager::ObserveTexture( TextureInfo& textureInfo,
308                                      TextureUploadObserver* observer )
309 {
310   if( observer )
311   {
312     textureInfo.observerList.PushBack( observer );
313     observer->DestructionSignal().Connect( this, &TextureManager::ObserverDestroyed );
314   }
315 }
316
317 void TextureManager::AsyncLocalLoadComplete( uint32_t id, PixelData pixelData )
318 {
319   AsyncLoadComplete( mAsyncLocalLoadingInfoContainer, id, pixelData );
320 }
321
322 void TextureManager::AsyncRemoteLoadComplete( uint32_t id, PixelData pixelData )
323 {
324   AsyncLoadComplete( mAsyncRemoteLoadingInfoContainer, id, pixelData );
325 }
326
327 void TextureManager::AsyncLoadComplete( AsyncLoadingInfoContainerType& loadingContainer, uint32_t id, PixelData pixelData )
328 {
329   DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( id:%d )\n", id );
330
331   if( loadingContainer.size() >= 1u )
332   {
333     AsyncLoadingInfo loadingInfo = loadingContainer.front();
334
335     if( loadingInfo.loadId == id )
336     {
337       int cacheIndex = GetCacheIndexFromId( loadingInfo.textureId );
338       if( cacheIndex != INVALID_CACHE_INDEX )
339       {
340         TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
341
342         DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "  CacheIndex:%d LoadState: %d\n", cacheIndex, textureInfo.loadState );
343
344         if( textureInfo.loadState != CANCELLED )
345         {
346           PostLoad( textureInfo, pixelData );
347         }
348         else
349         {
350           Remove( textureInfo.textureId );
351         }
352       }
353     }
354
355     loadingContainer.pop_front();
356   }
357 }
358
359 void TextureManager::PostLoad( TextureInfo& textureInfo, PixelData pixelData )
360 {
361   // Was the load successful?
362   if( pixelData && ( pixelData.GetWidth() != 0 ) && ( pixelData.GetHeight() != 0 ) )
363   {
364     // No atlas support for now
365     textureInfo.useAtlas = NO_ATLAS;
366
367     if( textureInfo.storageType == GPU_UPLOAD )
368     {
369       // If there is a mask texture ID associated with this texture, then apply the mask
370       // if it's already loaded. If it hasn't, and the mask is still loading,
371       // wait for the mask to finish loading.
372       if( textureInfo.maskTextureId != INVALID_TEXTURE_ID )
373       {
374         LoadState maskLoadState = GetTextureState( textureInfo.maskTextureId );
375         if( maskLoadState == LOADING )
376         {
377           textureInfo.pixelData = pixelData; // Store the pixel data temporarily
378           textureInfo.loadState = WAITING_FOR_MASK;
379         }
380         else if( maskLoadState == LOAD_FINISHED )
381         {
382           ApplyMask( pixelData, textureInfo.maskTextureId );
383           UploadTexture( pixelData, textureInfo );
384           NotifyObservers( textureInfo, true );
385         }
386       }
387       else
388       {
389         UploadTexture( pixelData, textureInfo );
390         NotifyObservers( textureInfo, true );
391       }
392     }
393     else // currently, CPU textures are local to texture manager
394     {
395       textureInfo.pixelData = pixelData; // Store the pixel data
396       textureInfo.loadState = LOAD_FINISHED;
397
398       // Check if there was another texture waiting for this load to complete
399       // (e.g. if this was an image mask, and its load is on a different thread)
400       CheckForWaitingTexture( textureInfo );
401     }
402   }
403   else
404   {
405     DALI_LOG_ERROR( "TextureManager::AsyncImageLoad(%s) failed\n", textureInfo.url.GetUrl().c_str() );
406     // @todo If the load was unsuccessful, upload the broken image.
407     textureInfo.loadState = LOAD_FAILED;
408     CheckForWaitingTexture( textureInfo );
409     NotifyObservers( textureInfo, false );
410   }
411 }
412
413 void TextureManager::CheckForWaitingTexture( TextureInfo& maskTextureInfo )
414 {
415   // Search the cache, checking if any texture has this texture id as a
416   // maskTextureId:
417   const unsigned int size = mTextureInfoContainer.size();
418
419   for( unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex )
420   {
421     if( mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
422         mTextureInfoContainer[cacheIndex].loadState == WAITING_FOR_MASK )
423     {
424       TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
425       PixelData pixelData = textureInfo.pixelData;
426       textureInfo.pixelData.Reset();
427
428       if( maskTextureInfo.loadState == LOAD_FINISHED )
429       {
430         ApplyMask( pixelData, maskTextureInfo.textureId );
431         UploadTexture( pixelData, textureInfo );
432         NotifyObservers( textureInfo, true );
433       }
434       else
435       {
436         DALI_LOG_ERROR( "TextureManager::ApplyMask to %s failed\n", textureInfo.url.GetUrl().c_str() );
437         textureInfo.loadState = LOAD_FAILED;
438         NotifyObservers( textureInfo, false );
439       }
440     }
441   }
442 }
443
444 void TextureManager::ApplyMask( PixelData pixelData, TextureId maskTextureId )
445 {
446   int maskCacheIndex = GetCacheIndexFromId( maskTextureId );
447   PixelData maskPixelData = mTextureInfoContainer[maskCacheIndex].pixelData;
448   Dali::ApplyMask( pixelData, maskPixelData );
449 }
450
451 void TextureManager::UploadTexture( PixelData pixelData, TextureInfo& textureInfo )
452 {
453   if( textureInfo.useAtlas != USE_ATLAS )
454   {
455     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "  TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId );
456
457     Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight() );
458     texture.Upload( pixelData );
459     textureInfo.textureSet = TextureSet::New();
460     textureInfo.textureSet.SetTexture( 0u, texture );
461   }
462
463   // Update the load state.
464   // Note: This is regardless of success as we care about whether a
465   // load attempt is in progress or not.  If unsuccessful, a broken
466   // image is still loaded.
467   textureInfo.loadState = UPLOADED;
468 }
469
470 void TextureManager::NotifyObservers( TextureInfo& textureInfo, bool success )
471 {
472   // If there is an observer: Notify the upload is complete
473   const unsigned int observerCount = textureInfo.observerList.Count();
474   for( unsigned int i = 0; i < observerCount; ++i )
475   {
476     TextureUploadObserver* observer = textureInfo.observerList[i];
477     if( observer )
478     {
479       observer->UploadComplete( success, textureInfo.textureSet, textureInfo.useAtlas, textureInfo.atlasRect );
480       observer->DestructionSignal().Disconnect( this, &TextureManager::ObserverDestroyed );
481     }
482   }
483
484   textureInfo.observerList.Clear();
485 }
486
487
488 TextureManager::TextureId TextureManager::GenerateUniqueTextureId()
489 {
490   return mCurrentTextureId++;
491 }
492
493 int TextureManager::GetCacheIndexFromId( const TextureId textureId )
494 {
495   const unsigned int size = mTextureInfoContainer.size();
496
497   for( unsigned int i = 0; i < size; ++i )
498   {
499     if( mTextureInfoContainer[i].textureId == textureId )
500     {
501       return i;
502     }
503   }
504
505   DALI_LOG_WARNING( "Cannot locate TextureId: %d\n", textureId );
506   return INVALID_CACHE_INDEX;
507 }
508
509 TextureManager::TextureHash TextureManager::GenerateHash(
510   const std::string&             url,
511   const ImageDimensions          size,
512   const FittingMode::Type        fittingMode,
513   const Dali::SamplingMode::Type samplingMode,
514   const UseAtlas                 useAtlas,
515   TextureId                      maskTextureId )
516 {
517   std::string hashTarget( url );
518   const size_t urlLength = hashTarget.length();
519   const uint16_t width = size.GetWidth();
520   const uint16_t height = size.GetWidth();
521
522   // If either the width or height has been specified, include the resizing options in the hash
523   if( width != 0 || height != 0 )
524   {
525     // We are appending 5 bytes to the URL to form the hash input.
526     hashTarget.resize( urlLength + 5u );
527     char* hashTargetPtr = &( hashTarget[ urlLength ] );
528
529     // Pack the width and height (4 bytes total).
530     *hashTargetPtr++ = size.GetWidth() & 0xff;
531     *hashTargetPtr++ = ( size.GetWidth() >> 8u ) & 0xff;
532     *hashTargetPtr++ = size.GetHeight() & 0xff;
533     *hashTargetPtr++ = ( size.GetHeight() >> 8u ) & 0xff;
534
535     // Bit-pack the FittingMode, SamplingMode and atlasing.
536     // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
537     *hashTargetPtr   = ( fittingMode << 4u ) | ( samplingMode << 1 ) | useAtlas;
538   }
539   else
540   {
541     // We are not including sizing information, but we still need an extra byte for atlasing.
542     hashTarget.resize( urlLength + 1u );
543     // Add the atlasing to the hash input.
544     hashTarget[ urlLength ] = useAtlas;
545   }
546
547   if( maskTextureId != INVALID_TEXTURE_ID )
548   {
549     hashTarget.resize( urlLength + sizeof( TextureId ) );
550     TextureId* hashTargetPtr = reinterpret_cast<TextureId*>(&( hashTarget[ urlLength ] ));
551
552     // Append the hash target to the end of the URL byte by byte:
553     // (to avoid SIGBUS / alignment issues)
554     for( size_t byteIter = 0; byteIter < sizeof( TextureId ); ++byteIter )
555     {
556       *hashTargetPtr++ = maskTextureId & 0xff;
557       maskTextureId >>= 8u;
558     }
559   }
560
561   return Dali::CalculateHash( hashTarget );
562 }
563
564 int TextureManager::FindCachedTexture(
565   const TextureManager::TextureHash hash,
566   const std::string&                url,
567   const ImageDimensions             size,
568   const FittingMode::Type           fittingMode,
569   const Dali::SamplingMode::Type    samplingMode,
570   const bool                        useAtlas,
571   TextureId                         maskTextureId)
572 {
573   // Default to an invalid ID, in case we do not find a match.
574   int cacheIndex = INVALID_CACHE_INDEX;
575
576   // Iterate through our hashes to find a match.
577   const unsigned int count = mTextureInfoContainer.size();
578   for( unsigned int i = 0u; i < count; ++i )
579   {
580     if( mTextureInfoContainer[i].hash == hash )
581     {
582       // We have a match, now we check all the original parameters in case of a hash collision.
583       TextureInfo& textureInfo( mTextureInfoContainer[i] );
584
585       if( ( url == textureInfo.url.GetUrl() ) &&
586           ( useAtlas == textureInfo.useAtlas ) &&
587           ( maskTextureId == textureInfo.maskTextureId ) &&
588           ( size == textureInfo.desiredSize ) &&
589           ( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
590             ( fittingMode == textureInfo.fittingMode &&
591               samplingMode == textureInfo.samplingMode ) ) )
592       {
593         // The found Texture is a match.
594         cacheIndex = i;
595         break;
596       }
597     }
598   }
599
600   return cacheIndex;
601 }
602
603 void TextureManager::ObserverDestroyed( TextureUploadObserver* observer )
604 {
605   const unsigned int count = mTextureInfoContainer.size();
606   for( unsigned int i = 0; i < count; ++i )
607   {
608     TextureInfo& textureInfo( mTextureInfoContainer[i] );
609     for( TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin(); j != textureInfo.observerList.End(); ++j )
610     {
611       if( *j == observer )
612       {
613         textureInfo.observerList.Erase( j );
614         break;
615       }
616     }
617   }
618 }
619
620 TextureManager::~TextureManager()
621 {
622   mTextureInfoContainer.clear();
623   mAsyncLocalLoadingInfoContainer.clear();
624   mAsyncRemoteLoadingInfoContainer.clear();
625 }
626
627
628
629
630 } // namespace Internal
631
632 } // namespace Toolkit
633
634 } // namespace Dali