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