Adding support for multiple images in AnimatedImageVisual
[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, textureInfo.textureId, textureInfo.textureSet,
173                                   textureInfo.useAtlas, 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     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::Remove(%d) cacheIdx:%d loadState:%s\n",
204                    textureId, textureInfoIndex,
205                    textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
206                    textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
207                    textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
208                    textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
209
210     // Decrement the reference count and check if this is the last user of this Texture.
211     if( --textureInfo.referenceCount <= 0 )
212     {
213       // This is the last remove for this Texture.
214       textureInfo.referenceCount = 0;
215       bool removeTextureInfo = false;
216
217       // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
218       if( textureInfo.loadState == UPLOADED )
219       {
220         if( textureInfo.atlas )
221         {
222           textureInfo.atlas.Remove( textureInfo.atlasRect );
223         }
224         removeTextureInfo = true;
225       }
226       else if( textureInfo.loadState == LOADING )
227       {
228         // We mark the textureInfo for removal.
229         // Once the load has completed, this method will be called again.
230         textureInfo.loadState = CANCELLED;
231       }
232       else
233       {
234         // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
235         removeTextureInfo = true;
236       }
237
238       // If the state allows us to remove the TextureInfo data, we do so.
239       if( removeTextureInfo )
240       {
241         // Permanently remove the textureInfo struct.
242         mTextureInfoContainer.erase( mTextureInfoContainer.begin() + textureInfoIndex );
243       }
244     }
245   }
246 }
247
248 const VisualUrl& TextureManager::GetVisualUrl( TextureId textureId )
249 {
250   int cacheIndex = GetCacheIndexFromId( textureId );
251   DALI_ASSERT_DEBUG( cacheIndex != INVALID_CACHE_INDEX && "TextureId out of range");
252
253   TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
254   return cachedTextureInfo.url;
255 }
256
257 TextureManager::LoadState TextureManager::GetTextureState( TextureId textureId )
258 {
259   LoadState loadState = TextureManager::NOT_STARTED;
260
261   int cacheIndex = GetCacheIndexFromId( textureId );
262   if( cacheIndex != INVALID_CACHE_INDEX )
263   {
264     TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
265     loadState = cachedTextureInfo.loadState;
266   }
267   return loadState;
268 }
269
270 TextureSet TextureManager::GetTextureSet( TextureId textureId )
271 {
272   TextureSet textureSet;// empty handle
273
274   int cacheIndex = GetCacheIndexFromId( textureId );
275   if( cacheIndex != INVALID_CACHE_INDEX )
276   {
277     TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
278     textureSet = cachedTextureInfo.textureSet;
279   }
280   return textureSet;
281 }
282
283 bool TextureManager::LoadTexture( TextureInfo& textureInfo )
284 {
285   bool success = true;
286
287   if( textureInfo.loadState == NOT_STARTED )
288   {
289     textureInfo.loadState = LOADING;
290
291     if( !textureInfo.loadSynchronously )
292     {
293       if( textureInfo.url.IsLocal() )
294       {
295         mAsyncLocalLoadingInfoContainer.push_back( AsyncLoadingInfo( textureInfo.textureId ) );
296         mAsyncLocalLoadingInfoContainer.back().loadId =
297           GetImplementation(mAsyncLocalLoader).Load( textureInfo.url, textureInfo.desiredSize,
298                                                      textureInfo.fittingMode,
299                                                      textureInfo.samplingMode, true );
300       }
301       else
302       {
303         mAsyncRemoteLoadingInfoContainer.push_back( AsyncLoadingInfo( textureInfo.textureId ) );
304         mAsyncRemoteLoadingInfoContainer.back().loadId =
305           GetImplementation(mAsyncRemoteLoader).Load( textureInfo.url, textureInfo.desiredSize,
306                                                       textureInfo.fittingMode,
307                                                       textureInfo.samplingMode, true );
308       }
309     }
310   }
311
312   return success;
313 }
314
315 void TextureManager::ObserveTexture( TextureInfo& textureInfo,
316                                      TextureUploadObserver* observer )
317 {
318   if( observer )
319   {
320     textureInfo.observerList.PushBack( observer );
321     observer->DestructionSignal().Connect( this, &TextureManager::ObserverDestroyed );
322   }
323 }
324
325 void TextureManager::AsyncLocalLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer )
326 {
327   AsyncLoadComplete( mAsyncLocalLoadingInfoContainer, id, pixelBuffer );
328 }
329
330 void TextureManager::AsyncRemoteLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer )
331 {
332   AsyncLoadComplete( mAsyncRemoteLoadingInfoContainer, id, pixelBuffer );
333 }
334
335 void TextureManager::AsyncLoadComplete( AsyncLoadingInfoContainerType& loadingContainer, uint32_t id, Devel::PixelBuffer pixelBuffer )
336 {
337   DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( id:%d )\n", id );
338
339   if( loadingContainer.size() >= 1u )
340   {
341     AsyncLoadingInfo loadingInfo = loadingContainer.front();
342
343     if( loadingInfo.loadId == id )
344     {
345       int cacheIndex = GetCacheIndexFromId( loadingInfo.textureId );
346       if( cacheIndex != INVALID_CACHE_INDEX )
347       {
348         TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
349
350         DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "  CacheIndex:%d LoadState: %d\n", cacheIndex, textureInfo.loadState );
351
352         if( textureInfo.loadState != CANCELLED )
353         {
354           // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified)
355           PostLoad( textureInfo, pixelBuffer );
356         }
357         else
358         {
359           Remove( textureInfo.textureId );
360         }
361       }
362     }
363
364     loadingContainer.pop_front();
365   }
366 }
367
368 void TextureManager::PostLoad( TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer )
369 {
370   // Was the load successful?
371   if( pixelBuffer && ( pixelBuffer.GetWidth() != 0 ) && ( pixelBuffer.GetHeight() != 0 ) )
372   {
373     // No atlas support for now
374     textureInfo.useAtlas = NO_ATLAS;
375
376     if( textureInfo.storageType == UPLOAD_TO_TEXTURE )
377     {
378       // If there is a mask texture ID associated with this texture, then apply the mask
379       // if it's already loaded. If it hasn't, and the mask is still loading,
380       // wait for the mask to finish loading.
381       if( textureInfo.maskTextureId != INVALID_TEXTURE_ID )
382       {
383         LoadState maskLoadState = GetTextureState( textureInfo.maskTextureId );
384         if( maskLoadState == LOADING )
385         {
386           textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily
387           textureInfo.loadState = WAITING_FOR_MASK;
388         }
389         else if( maskLoadState == LOAD_FINISHED )
390         {
391           ApplyMask( pixelBuffer, textureInfo.maskTextureId );
392           UploadTexture( pixelBuffer, textureInfo );
393           NotifyObservers( textureInfo, true );
394         }
395       }
396       else
397       {
398         UploadTexture( pixelBuffer, textureInfo );
399         NotifyObservers( textureInfo, true );
400       }
401     }
402     else
403     {
404       textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data
405       textureInfo.loadState = LOAD_FINISHED;
406
407       // Check if there was another texture waiting for this load to complete
408       // (e.g. if this was an image mask, and its load is on a different thread)
409       CheckForWaitingTexture( textureInfo );
410     }
411   }
412   else
413   {
414     DALI_LOG_ERROR( "TextureManager::AsyncImageLoad(%s) failed\n", textureInfo.url.GetUrl().c_str() );
415     // @todo If the load was unsuccessful, upload the broken image.
416     textureInfo.loadState = LOAD_FAILED;
417     CheckForWaitingTexture( textureInfo );
418     NotifyObservers( textureInfo, false );
419   }
420 }
421
422 void TextureManager::CheckForWaitingTexture( TextureInfo& maskTextureInfo )
423 {
424   // Search the cache, checking if any texture has this texture id as a
425   // maskTextureId:
426   const unsigned int size = mTextureInfoContainer.size();
427
428   for( unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex )
429   {
430     if( mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
431         mTextureInfoContainer[cacheIndex].loadState == WAITING_FOR_MASK )
432     {
433       TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
434       Devel::PixelBuffer pixelBuffer = textureInfo.pixelBuffer;
435       textureInfo.pixelBuffer.Reset();
436
437       if( maskTextureInfo.loadState == LOAD_FINISHED )
438       {
439         ApplyMask( pixelBuffer, maskTextureInfo.textureId );
440         UploadTexture( pixelBuffer, textureInfo );
441         NotifyObservers( textureInfo, true );
442       }
443       else
444       {
445         DALI_LOG_ERROR( "TextureManager::ApplyMask to %s failed\n", textureInfo.url.GetUrl().c_str() );
446         textureInfo.loadState = LOAD_FAILED;
447         NotifyObservers( textureInfo, false );
448       }
449     }
450   }
451 }
452
453 void TextureManager::ApplyMask( Devel::PixelBuffer& pixelBuffer, TextureId maskTextureId )
454 {
455   int maskCacheIndex = GetCacheIndexFromId( maskTextureId );
456   Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer;
457   pixelBuffer.ApplyMask( maskPixelBuffer );
458 }
459
460 void TextureManager::UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo )
461 {
462   if( textureInfo.useAtlas != USE_ATLAS )
463   {
464     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "  TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId );
465
466     Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight() );
467     PixelData pixelData = Devel::PixelBuffer::Convert( pixelBuffer );
468     texture.Upload( pixelData );
469     textureInfo.textureSet = TextureSet::New();
470     textureInfo.textureSet.SetTexture( 0u, texture );
471   }
472
473   // Update the load state.
474   // Note: This is regardless of success as we care about whether a
475   // load attempt is in progress or not.  If unsuccessful, a broken
476   // image is still loaded.
477   textureInfo.loadState = UPLOADED;
478 }
479
480 void TextureManager::NotifyObservers( TextureInfo& textureInfo, bool success )
481 {
482   TextureId textureId = textureInfo.textureId;
483
484   // If there is an observer: Notify the load is complete, whether successful or not,
485   // and erase it from the list
486   unsigned int observerCount = textureInfo.observerList.Count();
487   TextureInfo* info = &textureInfo;
488
489   while( observerCount )
490   {
491     TextureUploadObserver* observer = info->observerList[0];
492
493     // During UploadComplete() a Control ResourceReady() signal is emitted.
494     // During that signal the app may add remove /add Textures (e.g. via
495     // ImageViews).  At this point no more observers can be added to the
496     // observerList, because textureInfo.loadState = UPLOADED. However it is
497     // possible for observers to be removed, hence we check the observer list
498     // count every iteration.
499
500     // The reference to the textureInfo struct can also become invalidated,
501     // because new load requests can modify the mTextureInfoContainer list
502     // (e.g. if more requests are pushed back it can cause the list to be
503     // resized invalidating the reference to the TextureInfo ).
504     observer->UploadComplete( success, info->textureId, info->textureSet, info->useAtlas, info->atlasRect );
505     observer->DestructionSignal().Disconnect( this, &TextureManager::ObserverDestroyed );
506
507     // Get the textureInfo from the container again as it may have been
508     // invalidated,
509
510     int textureInfoIndex = GetCacheIndexFromId( textureId );
511     if( textureInfoIndex == INVALID_CACHE_INDEX)
512     {
513       return; // texture has been removed - can stop.
514     }
515
516     info = &mTextureInfoContainer[ textureInfoIndex ];
517     observerCount = info->observerList.Count();
518     if ( observerCount > 0 )
519     {
520       // remove the observer that was just triggered if it's still in the list
521       for( TextureInfo::ObserverListType::Iterator j = info->observerList.Begin(); j != info->observerList.End(); ++j )
522       {
523         if( *j == observer )
524         {
525           info->observerList.Erase( j );
526           observerCount--;
527           break;
528         }
529       }
530     }
531   }
532 }
533
534 TextureManager::TextureId TextureManager::GenerateUniqueTextureId()
535 {
536   return mCurrentTextureId++;
537 }
538
539 int TextureManager::GetCacheIndexFromId( const TextureId textureId )
540 {
541   const unsigned int size = mTextureInfoContainer.size();
542
543   for( unsigned int i = 0; i < size; ++i )
544   {
545     if( mTextureInfoContainer[i].textureId == textureId )
546     {
547       return i;
548     }
549   }
550
551   DALI_LOG_WARNING( "Cannot locate TextureId: %d\n", textureId );
552   return INVALID_CACHE_INDEX;
553 }
554
555 TextureManager::TextureHash TextureManager::GenerateHash(
556   const std::string&             url,
557   const ImageDimensions          size,
558   const FittingMode::Type        fittingMode,
559   const Dali::SamplingMode::Type samplingMode,
560   const UseAtlas                 useAtlas,
561   TextureId                      maskTextureId )
562 {
563   std::string hashTarget( url );
564   const size_t urlLength = hashTarget.length();
565   const uint16_t width = size.GetWidth();
566   const uint16_t height = size.GetWidth();
567
568   // If either the width or height has been specified, include the resizing options in the hash
569   if( width != 0 || height != 0 )
570   {
571     // We are appending 5 bytes to the URL to form the hash input.
572     hashTarget.resize( urlLength + 5u );
573     char* hashTargetPtr = &( hashTarget[ urlLength ] );
574
575     // Pack the width and height (4 bytes total).
576     *hashTargetPtr++ = size.GetWidth() & 0xff;
577     *hashTargetPtr++ = ( size.GetWidth() >> 8u ) & 0xff;
578     *hashTargetPtr++ = size.GetHeight() & 0xff;
579     *hashTargetPtr++ = ( size.GetHeight() >> 8u ) & 0xff;
580
581     // Bit-pack the FittingMode, SamplingMode and atlasing.
582     // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
583     *hashTargetPtr   = ( fittingMode << 4u ) | ( samplingMode << 1 ) | useAtlas;
584   }
585   else
586   {
587     // We are not including sizing information, but we still need an extra byte for atlasing.
588     hashTarget.resize( urlLength + 1u );
589     // Add the atlasing to the hash input.
590     hashTarget[ urlLength ] = useAtlas;
591   }
592
593   if( maskTextureId != INVALID_TEXTURE_ID )
594   {
595     hashTarget.resize( urlLength + sizeof( TextureId ) );
596     TextureId* hashTargetPtr = reinterpret_cast<TextureId*>(&( hashTarget[ urlLength ] ));
597
598     // Append the hash target to the end of the URL byte by byte:
599     // (to avoid SIGBUS / alignment issues)
600     for( size_t byteIter = 0; byteIter < sizeof( TextureId ); ++byteIter )
601     {
602       *hashTargetPtr++ = maskTextureId & 0xff;
603       maskTextureId >>= 8u;
604     }
605   }
606
607   return Dali::CalculateHash( hashTarget );
608 }
609
610 int TextureManager::FindCachedTexture(
611   const TextureManager::TextureHash hash,
612   const std::string&                url,
613   const ImageDimensions             size,
614   const FittingMode::Type           fittingMode,
615   const Dali::SamplingMode::Type    samplingMode,
616   const bool                        useAtlas,
617   TextureId                         maskTextureId)
618 {
619   // Default to an invalid ID, in case we do not find a match.
620   int cacheIndex = INVALID_CACHE_INDEX;
621
622   // Iterate through our hashes to find a match.
623   const unsigned int count = mTextureInfoContainer.size();
624   for( unsigned int i = 0u; i < count; ++i )
625   {
626     if( mTextureInfoContainer[i].hash == hash )
627     {
628       // We have a match, now we check all the original parameters in case of a hash collision.
629       TextureInfo& textureInfo( mTextureInfoContainer[i] );
630
631       if( ( url == textureInfo.url.GetUrl() ) &&
632           ( useAtlas == textureInfo.useAtlas ) &&
633           ( maskTextureId == textureInfo.maskTextureId ) &&
634           ( size == textureInfo.desiredSize ) &&
635           ( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
636             ( fittingMode == textureInfo.fittingMode &&
637               samplingMode == textureInfo.samplingMode ) ) )
638       {
639         // The found Texture is a match.
640         cacheIndex = i;
641         break;
642       }
643     }
644   }
645
646   return cacheIndex;
647 }
648
649 void TextureManager::ObserverDestroyed( TextureUploadObserver* observer )
650 {
651   const unsigned int count = mTextureInfoContainer.size();
652   for( unsigned int i = 0; i < count; ++i )
653   {
654     TextureInfo& textureInfo( mTextureInfoContainer[i] );
655     for( TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin(); j != textureInfo.observerList.End(); )
656     {
657       if( *j == observer )
658       {
659         j = textureInfo.observerList.Erase( j );
660       }
661       else
662       {
663         ++j;
664       }
665     }
666   }
667 }
668
669 TextureManager::~TextureManager()
670 {
671   mTextureInfoContainer.clear();
672   mAsyncLocalLoadingInfoContainer.clear();
673   mAsyncRemoteLoadingInfoContainer.clear();
674 }
675
676
677
678
679 } // namespace Internal
680
681 } // namespace Toolkit
682
683 } // namespace Dali