(ImageLoading) Added new method to get closest image size
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / slp / resource-loader / resource-loader.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 // CLASS HEADER
18 #include "resource-loader.h"
19
20 // EXTERNAL HEADERS
21 #include <boost/thread.hpp>
22 #include <iostream>
23 #include <fstream>
24 #include <set>
25 #include <queue>
26
27 // INTERNAL HEADERS
28 #include <dali/integration-api/bitmap.h>
29 #include <dali/integration-api/debug.h>
30 #include <dali/integration-api/resource-cache.h>
31 #include <dali/public-api/common/dali-common.h>
32 #include <dali/public-api/math/vector2.h>
33 #include "resource-requester-base.h"
34 #include "resource-bitmap-requester.h"
35 #include "resource-model-requester.h"
36 #include "resource-shader-requester.h"
37 #include "resource-text-requester.h"
38 #include "debug/resource-loader-debug.h"
39 #include "loader-font.h"
40 #include "../interfaces/font-controller.h"
41 #include "../interfaces/data-cache.h"
42
43
44 /**
45  * A macro to expand an argument to a compile time constant string literal.
46  * Wrapping the stringify in an outer macro, means that any macro passed as
47  * "x" will be expanded before being turned into a string.
48  * Use this for example to turn the current line number into a string:
49  *   puts("The current line number is " DALI_TO_STRING(__LINE__) ".");
50  */
51 #define DALI_TO_STRING_INNER(x) #x
52 #define DALI_TO_STRING(x) DALI_TO_STRING_INNER(x)
53
54 using namespace Dali::Integration;
55 using boost::mutex;
56 using boost::unique_lock;
57 using boost::scoped_ptr;
58
59 namespace Dali
60 {
61
62 namespace SlpPlatform
63 {
64
65 namespace
66 {
67
68 const char * const DALI_USER_FONT_CACHE_PATH( DALI_USER_FONT_CACHE_DIR );
69 const unsigned int MAX_NUMBER_CHARS_TO_CACHE( 60000 );  ///< support up to 60,000 glyphs
70 const unsigned int DISTANCE_FIELD_SIZE = 64;       // doesn't need to be power of two (the atlas may for performance)
71 const unsigned int DISTANCE_FIELD_PADDING = 30;    // Number of pixels of padding around the source FreeType bitmap
72 const unsigned int HIGH_QUALITY_PIXEL_SIZE = 200;  // Pixel size sent to FreeType2 FT_Set_Char_Size() for high quality glyphs
73 const float ONE_OVER_64 = 1.0f/64.0f;
74
75 #ifdef DEBUG_ENABLED
76 // For DEBUG_ENABLED profiling of distance field glyph generation
77 double GetTimeMicroseconds()
78 {
79   timespec time;
80   clock_gettime(CLOCK_MONOTONIC, &time);
81   double seconds = time.tv_sec;
82   seconds += 1e-3 * time.tv_nsec;
83   return seconds;
84 }
85 #endif
86
87 } // unnamed namespace
88
89
90 struct ResourceLoader::ResourceLoaderImpl
91 {
92   typedef std::pair<ResourceId, ResourceRequest>  RequestStorePair;
93   typedef std::map<ResourceId, ResourceRequest>   RequestStore;
94   typedef RequestStore::iterator                  RequestStoreIter;
95
96   typedef std::queue<LoadedResource> LoadedQueue;
97   typedef std::queue<SavedResource>  SavedQueue;
98   typedef std::queue<FailedResource> FailedQueue;
99
100   typedef std::pair<ResourceTypeId, ResourceRequesterBase*> RequestHandlerPair;
101   typedef std::map<ResourceTypeId,  ResourceRequesterBase*> RequestHandlers;
102   typedef RequestHandlers::iterator                         RequestHandlersIter;
103
104   boost::mutex mQueueMutex;             ///< used to synchronize access to mLoadedQueue, mSavedQueue and mFailedQueue
105   LoadedQueue  mPartiallyLoadedQueue;   ///< Partially complete load requests notifications are stored here until fetched by core
106   LoadedQueue  mLoadedQueue;            ///< Completed load requests notifications are stored here until fetched by core
107   SavedQueue   mSavedQueue;             ///< Completed save request notifications are stored here until fetched by core
108   FailedQueue  mFailedLoads;            ///< Failed load request notifications are stored here until fetched by core
109   FailedQueue  mFailedSaves;            ///< Failed save request notifications are stored here until fetched by core
110
111   Dali::Platform::FontController* mFontController;       ///< Interface for accessing font information
112
113   RequestHandlers mRequestHandlers;
114   RequestStore mStoredRequests;         ///< Used to store load requests until loading is completed
115
116   ResourceLoaderImpl( ResourceLoader* loader )
117   {
118     mFontController = Dali::Platform::FontController::New();
119
120     mRequestHandlers.insert(std::make_pair(ResourceBitmap, new ResourceBitmapRequester(*loader)));
121     mRequestHandlers.insert(std::make_pair(ResourceShader, new ResourceShaderRequester(*loader)));
122     mRequestHandlers.insert(std::make_pair(ResourceModel, new ResourceModelRequester(*loader)));
123     mRequestHandlers.insert(std::make_pair(ResourceText, new ResourceTextRequester(*loader)));
124   }
125
126   ~ResourceLoaderImpl()
127   {
128     // Delete resource handlers
129     for( RequestHandlersIter it = mRequestHandlers.begin(); it != mRequestHandlers.end(); ++it )
130     {
131       ResourceRequesterBase* requestBase = it->second;
132       delete requestBase;
133     }
134
135     delete mFontController;
136   }
137
138   void Pause()
139   {
140     // Pause all the request handlers:
141     for( RequestHandlersIter it = mRequestHandlers.begin(), end = mRequestHandlers.end(); it != end;  ++it )
142     {
143       ResourceRequesterBase * const requester = it->second;
144       if( requester )
145       {
146         requester->Pause();
147       }
148     }
149   }
150
151   void Resume()
152   {
153     // Wake up all the request handlers:
154     for( RequestHandlersIter it = mRequestHandlers.begin(), end = mRequestHandlers.end(); it != end;  ++it )
155     {
156       ResourceRequesterBase * const requester = it->second;
157       if( requester )
158       {
159         requester->Resume();
160       }
161     }
162   }
163
164   ResourceRequesterBase* GetRequester(ResourceTypeId typeId)
165   {
166     ResourceRequesterBase* requestHandler = NULL;
167     RequestHandlersIter iter = mRequestHandlers.find(typeId);
168     if(iter != mRequestHandlers.end())
169     {
170       requestHandler = iter->second;
171     }
172     DALI_ASSERT_DEBUG(requestHandler && "All resource types should have a requester defined for them.");
173     return requestHandler;
174   }
175
176   void LoadResource(const ResourceRequest& request)
177   {
178     // Store resource request for partial loaders. Will get cleaned up after load complete has finished
179     StoreRequest(request);
180
181     ResourceRequesterBase* requester = GetRequester(request.GetType()->id);
182     if( requester )
183     {
184       ResourceRequest* storedRequest = GetRequest(request.GetId());
185       if( storedRequest != NULL )
186       {
187         requester->LoadResource(*storedRequest); // Pass in stored request
188       }
189     }
190     else
191     {
192       DALI_LOG_ERROR( "Unknown resource type (%u) with path \"%s\" in load request.\n", request.GetType()->id, request.GetPath().c_str() );
193       DALI_ASSERT_DEBUG( 0 == "Unknown resource type in load request at " __FILE__ ", line " DALI_TO_STRING(__LINE__) ".\n" );
194     }
195   }
196
197   void SaveResource(const ResourceRequest& request)
198   {
199     ResourceRequesterBase* requester = GetRequester( request.GetType()->id );
200     if( requester )
201     {
202       requester->SaveResource( request );
203     }
204   }
205
206   void CancelLoad(ResourceId id, ResourceTypeId typeId)
207   {
208     ResourceRequesterBase* requester = GetRequester(typeId);
209     if( requester )
210     {
211       requester->CancelLoad( id, typeId );
212     }
213     ClearRequest( id );
214   }
215
216   LoadStatus LoadFurtherResources( LoadedResource partialResource )
217   {
218     LoadStatus loadStatus = RESOURCE_LOADING;
219     RequestStoreIter iter = mStoredRequests.find(partialResource.id);
220
221     if( mStoredRequests.end() != iter ) // else cancelled. Ignore response
222     {
223       ResourceRequest& request = iter->second;
224       ResourceRequesterBase* requester = GetRequester(request.GetType()->id);
225       if( requester )
226       {
227         loadStatus = requester->LoadFurtherResources( request, partialResource );
228       }
229
230       DALI_LOG_INFO(gLoaderFilter, Debug::General, "ResourceLoader::LoadFurtherResources( ID:%u complete: %s)\n",  request.GetId(), loadStatus==RESOURCE_LOADING?"Loading":loadStatus==RESOURCE_PARTIALLY_LOADED?"PARTIAL":"COMPLETE" );
231     }
232
233     if( loadStatus == RESOURCE_COMPLETELY_LOADED )
234     {
235       ClearRequest( partialResource.id );
236     }
237
238     return loadStatus;
239   }
240
241   bool IsLoading()
242   {
243     // TODO - not used - remove?
244     return true;
245   }
246
247   void GetResources(ResourceCache& cache)
248   {
249     // Fill the resource cache
250
251     unique_lock<mutex> lock(mQueueMutex);
252
253     // iterate through the partially loaded resources
254     while (!mPartiallyLoadedQueue.empty())
255     {
256       LoadedResource loaded( mPartiallyLoadedQueue.front() );
257       mPartiallyLoadedQueue.pop();
258       LoadStatus loadStatus = LoadFurtherResources( loaded );
259       cache.LoadResponse( loaded.id, loaded.type, loaded.resource, loadStatus );
260     }
261
262     // iterate through the successfully loaded resources
263     while (!mLoadedQueue.empty())
264     {
265       LoadedResource loaded( mLoadedQueue.front() );
266       mLoadedQueue.pop();
267       ClearRequest( loaded.id );
268       cache.LoadResponse( loaded.id, loaded.type, loaded.resource, RESOURCE_COMPLETELY_LOADED );
269     }
270
271     // iterate through the successfully saved resources
272     while (!mSavedQueue.empty())
273     {
274       SavedResource saved(mSavedQueue.front());
275       mSavedQueue.pop();
276       cache.SaveComplete(saved.id, saved.type);
277     }
278
279     // iterate through the resources which failed to load
280     while (!mFailedLoads.empty())
281     {
282       FailedResource failed(mFailedLoads.front());
283       mFailedLoads.pop();
284       ClearRequest(failed.id);
285       cache.LoadFailed(failed.id, failed.failureType);
286     }
287
288     // iterate through the resources which failed to save
289     while (!mFailedSaves.empty())
290     {
291       FailedResource failed(mFailedSaves.front());
292       mFailedSaves.pop();
293       cache.SaveFailed(failed.id, failed.failureType);
294     }
295   }
296
297   void GetClosestImageSize( const std::string& filename,
298                             const ImageAttributes& attributes,
299                             Vector2& closestSize )
300   {
301     ResourceRequesterBase* requester = GetRequester(ResourceBitmap);
302     ResourceBitmapRequester* bitmapRequester = dynamic_cast<ResourceBitmapRequester*>(requester);
303     if( bitmapRequester != NULL )
304     {
305       bitmapRequester->GetClosestImageSize( filename, attributes, closestSize );
306     }
307   }
308
309   void GetClosestImageSize( ResourcePointer resourceBuffer,
310                             const ImageAttributes& attributes,
311                             Vector2& closestSize )
312   {
313     ResourceRequesterBase* requester = GetRequester(ResourceBitmap);
314     ResourceBitmapRequester* bitmapRequester = dynamic_cast<ResourceBitmapRequester*>(requester);
315     if( bitmapRequester != NULL )
316     {
317       bitmapRequester->GetClosestImageSize( resourceBuffer, attributes, closestSize );
318     }
319   }
320
321   void AddPartiallyLoadedResource( LoadedResource& resource)
322   {
323     // Lock the LoadedQueue to store the loaded resource
324     unique_lock<mutex> lock(mQueueMutex);
325
326     mPartiallyLoadedQueue.push( resource );
327   }
328
329   void AddLoadedResource(LoadedResource& resource)
330   {
331     // Lock the LoadedQueue to store the loaded resource
332     unique_lock<mutex> lock(mQueueMutex);
333
334     mLoadedQueue.push( resource );
335   }
336
337   void AddSavedResource(SavedResource& resource)
338   {
339     // Lock the SavedQueue to store the loaded resource
340     unique_lock<mutex> lock(mQueueMutex);
341
342     mSavedQueue.push(resource);
343   }
344
345   void AddFailedLoad(FailedResource& resource)
346   {
347     // Lock the FailedQueue to store the failed resource information
348     unique_lock<mutex> lock(mQueueMutex);
349
350     mFailedLoads.push(resource);
351   }
352
353   void AddFailedSave(FailedResource& resource)
354   {
355     // Lock the FailedQueue to store the failed resource information
356     unique_lock<mutex> lock(mQueueMutex);
357
358     mFailedSaves.push(resource);
359   }
360
361   void StoreRequest( const ResourceRequest& request )
362   {
363     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader: StoreRequest(id:%u)\n", request.GetId());
364     mStoredRequests.insert( RequestStorePair( request.GetId(), request ) ); // copy request as value type
365   }
366
367   ResourceRequest* GetRequest( ResourceId id )
368   {
369     ResourceRequest* found(NULL);
370     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader: GetRequest(id:%u)\n", id);
371     RequestStoreIter iter = mStoredRequests.find( id );
372     if( mStoredRequests.end() != iter )
373     {
374       found = &iter->second;
375     }
376     return found;
377   }
378
379   void ClearRequest( ResourceId resourceId )
380   {
381     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader: ClearRequest(id:%u)\n", resourceId);
382     RequestStoreIter iter = mStoredRequests.find( resourceId );
383     if( mStoredRequests.end() != iter ) // Can't assert here - cancel load may cross with load failed
384     {
385       mStoredRequests.erase( iter );
386     }
387   }
388 };
389
390 /********************************************************************************/
391 /****************************   RESOURCE LOADER METHODS  ************************/
392 /********************************************************************************/
393 ResourceLoader::ResourceLoader()
394 : mTerminateThread(0)
395 {
396   mImpl = new ResourceLoaderImpl( this );
397 }
398
399 ResourceLoader::~ResourceLoader()
400 {
401   // Flag that the ResourceLoader is exiting
402   (void)__sync_or_and_fetch( &mTerminateThread, -1 );
403
404   delete mImpl;
405 }
406
407 void ResourceLoader::Pause()
408 {
409   mImpl->Pause();
410 }
411
412 void ResourceLoader::Resume()
413 {
414   mImpl->Resume();
415 }
416
417 bool ResourceLoader::IsTerminating()
418 {
419   return __sync_fetch_and_or( &mTerminateThread, 0 );
420 }
421
422 void ResourceLoader::GetResources(ResourceCache& cache)
423 {
424   mImpl->GetResources( cache );
425 }
426
427 /********************************************************************************/
428 /**************************   CALLED FROM LOADER THREADS   **********************/
429 /********************************************************************************/
430
431 void ResourceLoader::AddPartiallyLoadedResource( LoadedResource& resource)
432 {
433   mImpl->AddPartiallyLoadedResource( resource );
434 }
435
436 void ResourceLoader::AddLoadedResource(LoadedResource& resource)
437 {
438   mImpl->AddLoadedResource( resource );
439 }
440
441 void ResourceLoader::AddSavedResource(SavedResource& resource)
442 {
443   mImpl->AddSavedResource( resource );
444 }
445
446 void ResourceLoader::AddFailedLoad(FailedResource& resource)
447 {
448   mImpl->AddFailedLoad( resource );
449 }
450
451 void ResourceLoader::AddFailedSave(FailedResource& resource)
452 {
453   mImpl->AddFailedSave( resource );
454 }
455
456 /********************************************************************************/
457 /*********************   CALLED FROM PLATFORM ABSTRACTION  **********************/
458 /********************************************************************************/
459
460 void ResourceLoader::LoadResource(const ResourceRequest& request)
461 {
462   mImpl->LoadResource(request);
463 }
464
465 void ResourceLoader::SaveResource(const ResourceRequest& request)
466 {
467   mImpl->SaveResource(request);
468 }
469
470 void ResourceLoader::CancelLoad(ResourceId id, ResourceTypeId typeId)
471 {
472   mImpl->CancelLoad(id, typeId);
473 }
474
475 bool ResourceLoader::IsLoading()
476 {
477   return mImpl->IsLoading();
478 }
479
480 void ResourceLoader::GetClosestImageSize( const std::string& filename,
481                                           const ImageAttributes& attributes,
482                                           Vector2& closestSize )
483 {
484   mImpl->GetClosestImageSize( filename, attributes, closestSize );
485 }
486
487 void ResourceLoader::GetClosestImageSize( ResourcePointer resourceBuffer,
488                                           const ImageAttributes& attributes,
489                                           Vector2& closestSize )
490 {
491   mImpl->GetClosestImageSize( resourceBuffer, attributes, closestSize );
492 }
493
494
495 std::string ResourceLoader::GetFontFamilyForChars(const TextArray& charsRequested)
496 {
497   return mImpl->mFontController->GetFontFamilyForChars( charsRequested ).first;
498 }
499
500 bool ResourceLoader::AllGlyphsSupported(const std::string &fontFamily, const std::string &fontStyle, const TextArray& charsRequested)
501 {
502   return mImpl->mFontController->AllGlyphsSupported( Platform::FontController::StyledFontFamily( fontFamily, fontStyle ), charsRequested);
503
504 }
505
506 bool ResourceLoader::ValidateFontFamilyName(const std::string& fontFamily, const std::string& fontStyle, bool& isDefaultSystemFont, std::string& closestFontFamilyMatch, std::string& closestFontStyleMatch)
507 {
508   Platform::FontController::StyledFontFamily closestMatch;
509
510   bool result = mImpl->mFontController->ValidateFontFamilyName( Platform::FontController::StyledFontFamily( fontFamily, fontStyle ), isDefaultSystemFont, closestMatch);
511
512   closestFontFamilyMatch = closestMatch.first;
513   closestFontStyleMatch = closestMatch.second;
514
515   return result;
516 }
517
518 const PixelSize ResourceLoader::GetFontLineHeightFromCapsHeight(const std::string fontFamily, const std::string& fontStyle, const CapsHeight& capsHeight, FT_Library freeType)
519 {
520   PixelSize result(0);
521
522   if (!fontFamily.empty())
523   {
524     std::string fontFileName = GetFontPath( fontFamily, fontStyle );
525     SlpFace* slpFace = LoadFontFace(fontFileName, PixelSize(capsHeight), freeType);
526
527     if (slpFace)
528     {
529       const float scale = static_cast<float>(capsHeight.value) / ((slpFace->face->ascender / 64.0f) * 0.95f);
530
531       result.value = static_cast<unsigned int>(roundf(scale * (slpFace->face->height / 64.0f)));
532
533       delete slpFace;
534       slpFace = NULL;
535     }
536   }
537
538   return result;
539 }
540
541 std::vector<std::string> ResourceLoader::GetFontList( Dali::Integration::PlatformAbstraction::FontListMode mode )
542 {
543   std::vector<std::string> result;
544   std::set<std::string> uniqueFontNames;
545
546   // VCC TODO: A GetStyles() method which returns a list of styles for a given font family is needed.
547
548   Platform::FontController::FontList fontList;
549   Platform::FontController::FontListMode listMode;
550
551   switch( mode )
552   {
553     case Dali::Integration::PlatformAbstraction::LIST_ALL_FONTS:
554     {
555       listMode =  Platform::FontController::LIST_ALL_FONTS;
556       break;
557     }
558     case Dali::Integration::PlatformAbstraction::LIST_SYSTEM_FONTS:
559     {
560       listMode =  Platform::FontController::LIST_SYSTEM_FONTS;
561       break;
562     }
563     case Dali::Integration::PlatformAbstraction::LIST_APPLICATION_FONTS:
564     {
565       listMode =  Platform::FontController::LIST_APPLICATION_FONTS;
566       break;
567     }
568     default:
569     {
570       DALI_ASSERT_DEBUG(0 && "invalid mode");
571       return result;
572     }
573   }
574
575   fontList = mImpl->mFontController->GetFontList( listMode );
576
577   for( Platform::FontController::FontList::const_iterator it = fontList.begin(), endIt = fontList.end(); it != endIt; ++it )
578   {
579     uniqueFontNames.insert(it->first);
580   }
581
582   // copy into a vector
583   std::copy(uniqueFontNames.begin(), uniqueFontNames.end(), std::back_inserter(result));
584
585   return result;
586 }
587
588
589 /**
590  * Note, called from both platform abstraction & from text loader threads
591  **/
592 GlyphSet* ResourceLoader::GetGlyphData (const TextResourceType& textRequest,
593                                         FT_Library freeType,
594                                         const std::string& fontFamily,
595                                         bool getBitmap)
596 {
597   GlyphSet* glyphSet = NULL;
598
599   size_t fontHash = textRequest.mFontHash;
600
601   DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "LoadGlyphSet - requested string is %d characters long\n", textRequest.mCharacterList.size());
602
603   // path holds the font name
604   if( !fontFamily.empty() )
605   {
606     std::string fontFileName = GetFontPath( fontFamily, textRequest.mStyle );
607
608     const bool highQuality(textRequest.mQuality == TextResourceType::TextQualityHigh);
609     const unsigned int glyphQuality( highQuality ? GlyphMetrics::HIGH_QUALITY : GlyphMetrics::LOW_QUALITY );
610
611     SlpFace* slpFace = LoadFontFace( fontFileName, PixelSize( HIGH_QUALITY_PIXEL_SIZE), freeType );
612     if (slpFace)
613     {
614       glyphSet = new GlyphSet();
615       glyphSet->mFontHash = fontHash;
616       glyphSet->SetAtlasResourceId( textRequest.mTextureAtlasId );
617
618       for( TextResourceType::CharacterList::const_iterator it = textRequest.mCharacterList.begin(), endIt = textRequest.mCharacterList.end(); it != endIt; ++it )
619       {
620         uint32_t charCode( it->character );
621
622         if (!glyphSet->HasCharacter(charCode))        // ignore duplicate glyphs in the request
623         {
624 #ifdef DEBUG_ENABLED
625           // DEBUG_ENABLED profiling of distance field glyph generation
626           double then( 0.0 );
627           if( getBitmap )
628           {
629             then = GetTimeMicroseconds();
630           }
631 #endif
632           scoped_ptr< GlyphSet::Character > character( GetCharacter(slpFace->face, charCode,
633                                                                     DISTANCE_FIELD_SIZE, DISTANCE_FIELD_PADDING, textRequest.mMaxGlyphSize,
634                                                                     getBitmap, highQuality ) );
635
636 #ifdef DEBUG_ENABLED
637           // DEBUG_ENABLED profiling of distance field glyph generation
638           if( getBitmap )
639           {
640             double now( GetTimeMicroseconds() );
641
642             DALI_LOG_INFO( gLoaderFilter, Log::Verbose, "Generating (%c) in %s quality took %.3f ms\n", charCode, highQuality ? "high" : "low",  1e-3 * ( now - then ) );
643           }
644 #endif
645           if (character)
646           {
647             GlyphSet::Character& glyphCharacter( *character.get() );
648
649             glyphCharacter.second.quality = glyphQuality;
650             glyphCharacter.second.xPosition = it->xPosition;
651             glyphCharacter.second.yPosition = it->yPosition;
652             // copy character to GlyphSet
653             glyphSet->AddCharacter( glyphCharacter );
654           }
655         }
656       }
657
658       delete slpFace;
659     }
660   }
661
662   return glyphSet;
663 }
664
665 GlyphSet* ResourceLoader::GetCachedGlyphData(const TextResourceType& textRequest, const std::string& fontFamily)
666 {
667   GlyphSet* glyphSet( new GlyphSet() );
668   glyphSet->mFontHash = textRequest.mFontHash;
669   glyphSet->SetAtlasResourceId(textRequest.mTextureAtlasId);
670
671   std::string cachePath(DALI_USER_FONT_CACHE_PATH);
672   cachePath.append(fontFamily + "-" + textRequest.mStyle);
673   std::replace(cachePath.begin(), cachePath.end(), ' ', '-');
674
675   DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::GetCachedGlyphData() - cachefile: %s\n", cachePath.c_str() );
676
677   Platform::DataCache* dataCache = Platform::DataCache::New( Platform::DataCache::READ_ONLY,
678                                                              Platform::DataCache::RUN_LENGTH_ENCODING,
679                                                              cachePath,
680                                                              DISTANCE_FIELD_SIZE * DISTANCE_FIELD_SIZE,
681                                                              MAX_NUMBER_CHARS_TO_CACHE);
682
683   Platform::DataCache::KeyVector keyVector;
684   Platform::DataCache::DataVector dataVector;
685
686   const TextResourceType::CharacterList& requestedCharacters = textRequest.mCharacterList;
687   for( std::size_t i=0, length = requestedCharacters.size(); i < length; ++i )
688   {
689     keyVector.push_back( requestedCharacters[i].character );
690   }
691
692   // load the glyphs from file
693   dataCache->Find( keyVector, dataVector );
694
695   // for each glyph found, add to the glyph set
696   for( std::size_t n = 0, arraySize = keyVector.size(); n < arraySize ; n++ )
697   {
698     Platform::DataCache::Data& data( dataVector[n]);
699
700     if( data.exists )
701     {
702       GlyphMetrics glyphMetrics;
703       glyphMetrics.code = keyVector[ n ];
704       glyphMetrics.quality = GlyphMetrics::HIGH_QUALITY;
705       glyphMetrics.xPosition = requestedCharacters[n].xPosition;
706       glyphMetrics.yPosition = requestedCharacters[n].yPosition;
707
708       // create a new bitmap, and copy in the data
709       BitmapPtr bitmapData ( Integration::Bitmap::New(Bitmap::BITMAP_2D_PACKED_PIXELS, true) );
710       DALI_ASSERT_ALWAYS( data.length == DISTANCE_FIELD_SIZE * DISTANCE_FIELD_SIZE );
711
712       // assign the data
713       bitmapData->GetPackedPixelsProfile()->AssignBuffer( Pixel::A8, data.data, DISTANCE_FIELD_SIZE * DISTANCE_FIELD_SIZE, DISTANCE_FIELD_SIZE, DISTANCE_FIELD_SIZE );
714
715       data.data = NULL;
716
717       // add to the glyphset
718       glyphSet->AddCharacter( bitmapData, glyphMetrics );
719     }
720   }
721   DALI_LOG_INFO( gLoaderFilter, Debug::Verbose, "ResourceLoader::GetCachedGlyphData() - requestedGlyphs:%u, cachedGlyphs:%u\n",
722                  requestedCharacters.size(), glyphSet->GetCharacterList().size() );
723
724   delete dataCache;
725
726   return glyphSet;
727 }
728
729 void ResourceLoader::GetGlobalMetrics( FT_Library freeType,
730                                        const std::string& fontFamily,
731                                        const std::string& fontStyle,
732                                        GlobalMetrics& globalMetrics )
733 {
734   // path holds the font name
735   if( !fontFamily.empty() )
736   {
737     std::string fontFileName = GetFontPath( fontFamily, fontStyle );
738
739     SlpFace* slpFace = LoadFontFace( fontFileName, PixelSize( HIGH_QUALITY_PIXEL_SIZE), freeType );
740     if( slpFace )
741     {
742       // scale factor for unit scaled glyphs
743       const float xScale = 1.0f / (slpFace->face->size->metrics.x_scale / 65536.0f);
744       const float yScale = 1.0f / (slpFace->face->size->metrics.y_scale / 65536.0f);
745
746       globalMetrics.lineHeight = slpFace->face->height * ONE_OVER_64;
747       globalMetrics.ascender = slpFace->face->ascender * ONE_OVER_64;
748       globalMetrics.unitsPerEM = slpFace->face->units_per_EM * ONE_OVER_64;
749
750       globalMetrics.underlinePosition = -4.f;
751       globalMetrics.underlineThickness = 5.f * yScale;
752       if( 1.f > globalMetrics.underlineThickness )
753       {
754         globalMetrics.underlineThickness = 1.f;
755       }
756       globalMetrics.maxWidth = DISTANCE_FIELD_SIZE * xScale;
757       globalMetrics.maxHeight = DISTANCE_FIELD_SIZE * yScale;
758       globalMetrics.padAdjustX = DISTANCE_FIELD_PADDING * xScale;
759       globalMetrics.padAdjustY = DISTANCE_FIELD_PADDING * yScale;
760
761       delete slpFace;
762     }
763   }
764 }
765
766 void ResourceLoader::SetDpi(unsigned int dpiHor, unsigned int dpiVer)
767 {
768   // Unused
769 }
770
771 bool ResourceLoader::LoadFile( const std::string& filename, std::vector< unsigned char >& buffer ) const
772 {
773   DALI_LOG_TRACE_METHOD(gLoaderFilter);
774
775   DALI_ASSERT_DEBUG( 0 != filename.length());
776
777   bool result;
778
779   std::filebuf buf;
780   buf.open(filename.c_str(), std::ios::in | std::ios::binary);
781   if( buf.is_open() )
782   {
783     std::istream stream(&buf);
784
785     // determine data length
786     stream.seekg(0, std::ios_base::end);
787     unsigned int length = static_cast<unsigned int>( stream.tellg() );
788     stream.seekg(0, std::ios_base::beg);
789
790     // allocate a buffer
791     buffer.resize(length);
792     // read data into buffer
793     stream.read(reinterpret_cast<char*>(buffer.data()), length);
794
795     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::LoadFile(%s) - loaded %d bytes\n", filename.c_str(), length);
796
797     result = true;
798   }
799   else
800   {
801     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::LoadFile(%s) - failed to load\n", filename.c_str());
802     result = false;
803   }
804
805   return result;
806 }
807
808 std::string ResourceLoader::LoadFile(const std::string& filename) const
809 {
810   DALI_LOG_TRACE_METHOD(gLoaderFilter);
811
812   DALI_ASSERT_DEBUG( 0 != filename.length());
813
814   std::string contents;
815
816   std::filebuf buf;
817   buf.open(filename.c_str(), std::ios::in);
818   if( buf.is_open() )
819   {
820     std::istream stream(&buf);
821
822     // determine data length
823     stream.seekg(0, std::ios_base::end);
824     unsigned int length = static_cast<unsigned int>( stream.tellg() );
825     stream.seekg(0, std::ios_base::beg);
826
827     // allocate a buffer
828     contents.resize(length);
829     // read data into buffer
830     stream.read(&contents[0], length);
831
832     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::LoadFile(%s) - loaded %d bytes\n", filename.c_str(), length);
833   }
834   else
835   {
836     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::LoadFile(%s) - failed to load\n", filename.c_str());
837   }
838
839   return contents;
840 }
841
842 bool ResourceLoader::SaveFile(const std::string& filename, std::vector< unsigned char >& buffer)
843 {
844   DALI_LOG_TRACE_METHOD(gLoaderFilter);
845
846   DALI_ASSERT_DEBUG( 0 != filename.length());
847
848   bool result = false;
849
850   std::filebuf buf;
851   buf.open(filename.c_str(), std::ios::out | std::ios_base::trunc | std::ios::binary);
852   if( buf.is_open() )
853   {
854     std::ostream stream(&buf);
855
856     // determine size of buffer
857     int length = static_cast<int>(buffer.size());
858
859     // write contents of buffer to the file
860     stream.write(reinterpret_cast<char*>(buffer.data()), length);
861
862     if( !stream.bad() )
863     {
864       DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::SaveFile(%s) - wrote %d bytes\n", filename.c_str(), length);
865       result = true;
866     }
867   }
868
869 #if defined(DEBUG_BUILD)
870   if( !result )
871   {
872     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::SaveFile(%s) - failed to load\n", filename.c_str());
873   }
874 #endif
875
876   return result;
877 }
878
879 void ResourceLoader::SetDefaultFontFamily( const std::string& fontFamily, const std::string& fontStyle )
880 {
881   mImpl->mFontController->SetDefaultFontFamily( Platform::FontController::StyledFontFamily( fontFamily, fontStyle ) );
882 }
883
884 std::string ResourceLoader::GetFontPath(const std::string& fontFamily, const std::string& fontStyle)
885 {
886   return mImpl->mFontController->GetFontPath(std::make_pair(fontFamily,fontStyle));
887 }
888
889 } // namespace SlpPlatform
890
891 } // namespace Dali