Merge "Fixed automatic naming of visuals for non-native controls" 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           // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified)
347           PostLoad( textureInfo, pixelData );
348         }
349         else
350         {
351           Remove( textureInfo.textureId );
352         }
353       }
354     }
355
356     loadingContainer.pop_front();
357   }
358 }
359
360 void TextureManager::PostLoad( TextureInfo& textureInfo, PixelData pixelData )
361 {
362   // Was the load successful?
363   if( pixelData && ( pixelData.GetWidth() != 0 ) && ( pixelData.GetHeight() != 0 ) )
364   {
365     // No atlas support for now
366     textureInfo.useAtlas = NO_ATLAS;
367
368     if( textureInfo.storageType == GPU_UPLOAD )
369     {
370       // If there is a mask texture ID associated with this texture, then apply the mask
371       // if it's already loaded. If it hasn't, and the mask is still loading,
372       // wait for the mask to finish loading.
373       if( textureInfo.maskTextureId != INVALID_TEXTURE_ID )
374       {
375         LoadState maskLoadState = GetTextureState( textureInfo.maskTextureId );
376         if( maskLoadState == LOADING )
377         {
378           textureInfo.pixelData = pixelData; // Store the pixel data temporarily
379           textureInfo.loadState = WAITING_FOR_MASK;
380         }
381         else if( maskLoadState == LOAD_FINISHED )
382         {
383           ApplyMask( pixelData, textureInfo.maskTextureId );
384           UploadTexture( pixelData, textureInfo );
385           NotifyObservers( textureInfo, true );
386         }
387       }
388       else
389       {
390         UploadTexture( pixelData, textureInfo );
391         NotifyObservers( textureInfo, true );
392       }
393     }
394     else // currently, CPU textures are local to texture manager
395     {
396       textureInfo.pixelData = pixelData; // Store the pixel data
397       textureInfo.loadState = LOAD_FINISHED;
398
399       // Check if there was another texture waiting for this load to complete
400       // (e.g. if this was an image mask, and its load is on a different thread)
401       CheckForWaitingTexture( textureInfo );
402     }
403   }
404   else
405   {
406     DALI_LOG_ERROR( "TextureManager::AsyncImageLoad(%s) failed\n", textureInfo.url.GetUrl().c_str() );
407     // @todo If the load was unsuccessful, upload the broken image.
408     textureInfo.loadState = LOAD_FAILED;
409     CheckForWaitingTexture( textureInfo );
410     NotifyObservers( textureInfo, false );
411   }
412 }
413
414 void TextureManager::CheckForWaitingTexture( TextureInfo& maskTextureInfo )
415 {
416   // Search the cache, checking if any texture has this texture id as a
417   // maskTextureId:
418   const unsigned int size = mTextureInfoContainer.size();
419
420   for( unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex )
421   {
422     if( mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
423         mTextureInfoContainer[cacheIndex].loadState == WAITING_FOR_MASK )
424     {
425       TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
426       PixelData pixelData = textureInfo.pixelData;
427       textureInfo.pixelData.Reset();
428
429       if( maskTextureInfo.loadState == LOAD_FINISHED )
430       {
431         ApplyMask( pixelData, maskTextureInfo.textureId );
432         UploadTexture( pixelData, textureInfo );
433         NotifyObservers( textureInfo, true );
434       }
435       else
436       {
437         DALI_LOG_ERROR( "TextureManager::ApplyMask to %s failed\n", textureInfo.url.GetUrl().c_str() );
438         textureInfo.loadState = LOAD_FAILED;
439         NotifyObservers( textureInfo, false );
440       }
441     }
442   }
443 }
444
445 void TextureManager::ApplyMask( PixelData pixelData, TextureId maskTextureId )
446 {
447   int maskCacheIndex = GetCacheIndexFromId( maskTextureId );
448   PixelData maskPixelData = mTextureInfoContainer[maskCacheIndex].pixelData;
449   Dali::ApplyMask( pixelData, maskPixelData );
450 }
451
452 void TextureManager::UploadTexture( PixelData pixelData, TextureInfo& textureInfo )
453 {
454   if( textureInfo.useAtlas != USE_ATLAS )
455   {
456     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "  TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId );
457
458     Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight() );
459     texture.Upload( pixelData );
460     textureInfo.textureSet = TextureSet::New();
461     textureInfo.textureSet.SetTexture( 0u, texture );
462   }
463
464   // Update the load state.
465   // Note: This is regardless of success as we care about whether a
466   // load attempt is in progress or not.  If unsuccessful, a broken
467   // image is still loaded.
468   textureInfo.loadState = UPLOADED;
469 }
470
471 void TextureManager::NotifyObservers( TextureInfo& textureInfo, bool success )
472 {
473   TextureId textureId = textureInfo.textureId;
474
475   // If there is an observer: Notify the load is complete, whether successful or not:
476   // And erase it from the list
477   unsigned int observerCount = textureInfo.observerList.Count();
478   TextureInfo* info = &textureInfo;
479
480   while( observerCount )
481   {
482     TextureUploadObserver* observer = info->observerList[0];
483
484     // During UploadComplete() a Control ResourceReady() signal is emitted
485     // During that signal the app may add remove /add Textures (e.g. via ImageViews).
486     // At this point no more observers can be added to the observerList, because  textureInfo.loadState = UPLOADED
487     // However it is possible for observers to be removed, hence we check the observer list count every iteration
488
489     // Also the reference to the textureInfo struct can become invalidated, because new load requests can modify
490     // the mTextureInfoContainer list (e.g. if more requests are pushed_back it can cause the list to be resized
491     // invalidating the reference to the TextureInfo ).
492     observer->UploadComplete( success, info->textureSet, info->useAtlas, info->atlasRect );
493     observer->DestructionSignal().Disconnect( this, &TextureManager::ObserverDestroyed );
494
495     // regrab the textureInfo from the container as it may have been invalidated, if textures have been removed
496     // or added during the ResourceReady() signal emission (from UploadComplete() )
497     int textureInfoIndex = GetCacheIndexFromId( textureId );
498
499     if( textureInfoIndex == INVALID_CACHE_INDEX)
500     {
501       // texture has been removed
502       return;
503     }
504     info = &mTextureInfoContainer[ textureInfoIndex ];
505     observerCount = info->observerList.Count();
506     if ( observerCount > 0 )
507     {
508       // remove the observer that was just triggered if it's still in the list
509       for( TextureInfo::ObserverListType::Iterator j = info->observerList.Begin(); j != info->observerList.End(); ++j )
510       {
511         if( *j == observer )
512         {
513           info->observerList.Erase( j );
514           observerCount--;
515           break;
516          }
517       }
518     }
519
520   }
521
522 }
523
524
525 TextureManager::TextureId TextureManager::GenerateUniqueTextureId()
526 {
527   return mCurrentTextureId++;
528 }
529
530 int TextureManager::GetCacheIndexFromId( const TextureId textureId )
531 {
532   const unsigned int size = mTextureInfoContainer.size();
533
534   for( unsigned int i = 0; i < size; ++i )
535   {
536     if( mTextureInfoContainer[i].textureId == textureId )
537     {
538       return i;
539     }
540   }
541
542   DALI_LOG_WARNING( "Cannot locate TextureId: %d\n", textureId );
543   return INVALID_CACHE_INDEX;
544 }
545
546 TextureManager::TextureHash TextureManager::GenerateHash(
547   const std::string&             url,
548   const ImageDimensions          size,
549   const FittingMode::Type        fittingMode,
550   const Dali::SamplingMode::Type samplingMode,
551   const UseAtlas                 useAtlas,
552   TextureId                      maskTextureId )
553 {
554   std::string hashTarget( url );
555   const size_t urlLength = hashTarget.length();
556   const uint16_t width = size.GetWidth();
557   const uint16_t height = size.GetWidth();
558
559   // If either the width or height has been specified, include the resizing options in the hash
560   if( width != 0 || height != 0 )
561   {
562     // We are appending 5 bytes to the URL to form the hash input.
563     hashTarget.resize( urlLength + 5u );
564     char* hashTargetPtr = &( hashTarget[ urlLength ] );
565
566     // Pack the width and height (4 bytes total).
567     *hashTargetPtr++ = size.GetWidth() & 0xff;
568     *hashTargetPtr++ = ( size.GetWidth() >> 8u ) & 0xff;
569     *hashTargetPtr++ = size.GetHeight() & 0xff;
570     *hashTargetPtr++ = ( size.GetHeight() >> 8u ) & 0xff;
571
572     // Bit-pack the FittingMode, SamplingMode and atlasing.
573     // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
574     *hashTargetPtr   = ( fittingMode << 4u ) | ( samplingMode << 1 ) | useAtlas;
575   }
576   else
577   {
578     // We are not including sizing information, but we still need an extra byte for atlasing.
579     hashTarget.resize( urlLength + 1u );
580     // Add the atlasing to the hash input.
581     hashTarget[ urlLength ] = useAtlas;
582   }
583
584   if( maskTextureId != INVALID_TEXTURE_ID )
585   {
586     hashTarget.resize( urlLength + sizeof( TextureId ) );
587     TextureId* hashTargetPtr = reinterpret_cast<TextureId*>(&( hashTarget[ urlLength ] ));
588
589     // Append the hash target to the end of the URL byte by byte:
590     // (to avoid SIGBUS / alignment issues)
591     for( size_t byteIter = 0; byteIter < sizeof( TextureId ); ++byteIter )
592     {
593       *hashTargetPtr++ = maskTextureId & 0xff;
594       maskTextureId >>= 8u;
595     }
596   }
597
598   return Dali::CalculateHash( hashTarget );
599 }
600
601 int TextureManager::FindCachedTexture(
602   const TextureManager::TextureHash hash,
603   const std::string&                url,
604   const ImageDimensions             size,
605   const FittingMode::Type           fittingMode,
606   const Dali::SamplingMode::Type    samplingMode,
607   const bool                        useAtlas,
608   TextureId                         maskTextureId)
609 {
610   // Default to an invalid ID, in case we do not find a match.
611   int cacheIndex = INVALID_CACHE_INDEX;
612
613   // Iterate through our hashes to find a match.
614   const unsigned int count = mTextureInfoContainer.size();
615   for( unsigned int i = 0u; i < count; ++i )
616   {
617     if( mTextureInfoContainer[i].hash == hash )
618     {
619       // We have a match, now we check all the original parameters in case of a hash collision.
620       TextureInfo& textureInfo( mTextureInfoContainer[i] );
621
622       if( ( url == textureInfo.url.GetUrl() ) &&
623           ( useAtlas == textureInfo.useAtlas ) &&
624           ( maskTextureId == textureInfo.maskTextureId ) &&
625           ( size == textureInfo.desiredSize ) &&
626           ( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
627             ( fittingMode == textureInfo.fittingMode &&
628               samplingMode == textureInfo.samplingMode ) ) )
629       {
630         // The found Texture is a match.
631         cacheIndex = i;
632         break;
633       }
634     }
635   }
636
637   return cacheIndex;
638 }
639
640 void TextureManager::ObserverDestroyed( TextureUploadObserver* observer )
641 {
642   const unsigned int count = mTextureInfoContainer.size();
643   for( unsigned int i = 0; i < count; ++i )
644   {
645     TextureInfo& textureInfo( mTextureInfoContainer[i] );
646     for( TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin(); j != textureInfo.observerList.End(); ++j )
647     {
648       if( *j == observer )
649       {
650         textureInfo.observerList.Erase( j );
651         break;
652       }
653     }
654   }
655 }
656
657 TextureManager::~TextureManager()
658 {
659   mTextureInfoContainer.clear();
660   mAsyncLocalLoadingInfoContainer.clear();
661   mAsyncRemoteLoadingInfoContainer.clear();
662 }
663
664
665
666
667 } // namespace Internal
668
669 } // namespace Toolkit
670
671 } // namespace Dali