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