License conversion from Flora to Apache 2.0
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / slp / resource-loader / resource-thread-text.cpp
1 /*
2  * Copyright (c) 2014 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 "resource-thread-text.h"
20
21 // INTERNAL INCLUDES
22 #include "loader-font.h"
23 #include "../interfaces/data-cache.h"
24 #include "../data-cache/tests/data-cache-debug.h"
25
26 // EXTERNAL INCLUDES
27 #include <boost/functional/hash.hpp>
28 #include <ft2build.h>
29 #include <iostream>
30 #include FT_FREETYPE_H
31 #include FT_GLYPH_H
32
33 #include <dali/integration-api/glyph-set.h>
34 #include <dali/integration-api/platform-abstraction.h>
35
36
37 using namespace Dali::Integration;
38
39 namespace Dali
40 {
41 namespace SlpPlatform
42 {
43
44 namespace
45 {
46
47 const char * const DALI_USER_FONT_CACHE_PATH( DALI_USER_FONT_CACHE_DIR );
48 const unsigned int DISTANCE_FIELD_WIDTH( 64 );
49 const unsigned int DISTANCE_FIELD_HEIGHT( 64 );
50 const unsigned int DISTANCE_FIELD_SIZE(DISTANCE_FIELD_WIDTH * DISTANCE_FIELD_HEIGHT);
51 const unsigned int MAX_NUMBER_CHARS_TO_CACHE( 60000 );  ///< support up to 60,000 glyphs
52
53 boost::mutex mFileCheckMutex;             ///< used to ensure only one thread checks the files for corruption on startup
54
55
56 }; // unnamed namespace
57
58 ResourceThreadText::ResourceThreadText(ResourceLoader& resourceLoader, const TextResourceType::TextQuality quality)
59 : ResourceThreadBase(resourceLoader),
60   mQuality(quality),
61   mFreeTypeHandle( NULL )
62 {
63
64 #ifdef DATA_CACHE_DEBUG
65   DataCacheIo::ThreadedStressTest();
66 #endif
67 }
68
69 ResourceThreadText::~ResourceThreadText()
70 {
71   TerminateThread();
72
73   DeleteDataCaches();
74
75   if( mFreeTypeHandle )
76   {
77     FT_Done_FreeType(mFreeTypeHandle);
78   }
79 }
80
81 void ResourceThreadText::Load(const ResourceRequest& request)
82 {
83   // 1: Check if glyphs are in the data cache
84   // 2: For any missing glyphs, load from FreeType and save to cache file.
85   //
86   // Important Note: FreeType will never fail to load a character, if the character code
87   // is not part of the font it will render a 'default glyph'.
88   //
89   // This means we never call mResourceLoader.AddFailedLoad( ...)
90
91   const TextResourceType& textRequest = dynamic_cast<const TextResourceType&>(*request.GetType());
92   TextResourceType::CharacterList text(textRequest.mCharacterList);
93
94   std::string cachePath(DALI_USER_FONT_CACHE_PATH);
95   cachePath.append(request.GetPath() + "-" + textRequest.mStyle);
96   std::replace(cachePath.begin(), cachePath.end(), ' ', '-');
97
98   Platform::DataCache* dataCache = GetDataCache( cachePath );
99
100   GlyphSet *glyphSet( new GlyphSet() );
101   glyphSet->mFontHash = textRequest.mFontHash;
102   glyphSet->SetAtlasResourceId(textRequest.mTextureAtlasId);
103
104   if( textRequest.mCache == TextResourceType::GLYPH_CACHE_READ )
105   {
106     LoadCharactersFromCache( *dataCache, *glyphSet, text );
107   }
108   else
109   {
110     LoadCharacters( *dataCache, *glyphSet, textRequest, request.GetPath(), text );
111   }
112
113   IntrusivePtr<GlyphSet> glyphResource( glyphSet );
114   LoadedResource resource( request.GetId(), request.GetType()->id, glyphResource );
115   mResourceLoader.AddPartiallyLoadedResource(resource);
116 }
117
118 void ResourceThreadText::Save(const ResourceRequest& request)
119 {
120 }
121
122 void ResourceThreadText::LoadCharactersFromCache(
123   Platform::DataCache& dataCache,
124   GlyphSet& glyphSet,
125   const TextResourceType::CharacterList& requestedCharacters )
126 {
127   Platform::DataCache::KeyVector keyVector;
128   for( std::size_t i=0, length=requestedCharacters.size(); i<length; i++ )
129   {
130     keyVector.push_back(requestedCharacters[i].character);
131   }
132   Platform::DataCache::DataVector dataVector;
133
134   // load the glyphs from file
135   dataCache.Find( keyVector, dataVector );
136
137
138   // for each glyph found, add to the glyph set
139   for( std::size_t n = 0, arraySize = keyVector.size(); n < arraySize ; n++ )
140   {
141     Platform::DataCache::Data& data( dataVector[n]);
142
143     if( data.exists )
144     {
145       GlyphMetrics glyphMetrics;
146       glyphMetrics.code = keyVector[ n ];
147       glyphMetrics.quality = GlyphMetrics::HIGH_QUALITY;
148       glyphMetrics.xPosition = requestedCharacters[n].xPosition;
149       glyphMetrics.yPosition = requestedCharacters[n].yPosition;
150
151       // create a new bitmap, and copy in the data
152       BitmapPtr bitmapData ( Integration::Bitmap::New(Bitmap::BITMAP_2D_PACKED_PIXELS, true) );
153       DALI_ASSERT_ALWAYS( data.length == DISTANCE_FIELD_SIZE );
154
155       // assign the data
156       bitmapData->GetPackedPixelsProfile()->AssignBuffer( Pixel::A8, data.data, DISTANCE_FIELD_SIZE, DISTANCE_FIELD_WIDTH, DISTANCE_FIELD_HEIGHT );
157
158       data.data = NULL;
159
160       // add to the glyphset
161       glyphSet.AddCharacter( bitmapData, glyphMetrics );
162     }
163   }
164 }
165
166 void ResourceThreadText::SaveCharacters( Platform::DataCache& dataCache, const GlyphSet& glyphSet )
167 {
168   const GlyphSet::CharacterList& chars = glyphSet.GetCharacterList();
169
170   Platform::DataCache::KeyVector keyVector;
171   Platform::DataCache::DataVector dataVector;
172
173   keyVector.reserve( chars.size() );
174   dataVector.reserve( chars.size() );
175
176   for( GlyphSet::CharacterList::const_iterator it = chars.begin(), endIt = chars.end(); it != endIt; ++it )
177   {
178     const BitmapPtr& bitmap( it->first );
179     if ( bitmap )
180     {
181       const GlyphMetrics& metrics( it->second );
182
183       Platform::DataCache::DataKey key( metrics.code );
184       Platform::DataCache::Data data;
185       data.SetData( bitmap->GetBuffer(), DISTANCE_FIELD_SIZE );
186
187       keyVector.push_back( key );
188       dataVector.push_back( data );
189     }
190   }
191
192   // this will save the distance fields to file
193   dataCache.Add( keyVector, dataVector );
194
195 }
196
197 void ResourceThreadText::LoadCharacters( Platform::DataCache& dataCache,
198                                          GlyphSet& glyphSet,
199                                          const TextResourceType& textRequest,
200                                          const std::string& path,
201                                          const TextResourceType::CharacterList& charCodes )
202 {
203   // we need to use freetype, ensure the library has been initialised
204   LoadFreeType();
205
206   // baseline and other general info still queried from FreeType even if all characters were found in cache
207   GlyphSet* missingGlyphSet = mResourceLoader.GetGlyphData(textRequest, mFreeTypeHandle, path, true);
208
209   const GlyphSet::CharacterList& cachedChars = missingGlyphSet->GetCharacterList();
210   for( GlyphSet::CharacterList::const_iterator it = cachedChars.begin(), endIt = cachedChars.end(); it != endIt; ++it )
211   {
212     glyphSet.AddCharacter( *it );
213   }
214
215   // save to cache if high quality
216   if( ( mQuality == TextResourceType::TextQualityHigh ) && ( TextResourceType::GLYPH_CACHE_WRITE == textRequest.mCache ) )
217   {
218     SaveCharacters( dataCache, glyphSet );
219   }
220
221   // all the information in missingGlyphSet has been copied, so delete it.
222   delete missingGlyphSet;
223 }
224
225 void ResourceThreadText::LoadFreeType()
226 {
227   if( mFreeTypeHandle )
228   {
229     return;
230   }
231
232   if( FT_Init_FreeType( &mFreeTypeHandle ) != 0 )
233   {
234     DALI_ASSERT_ALWAYS( 0 && "FT_Init_FreeType failed\n");
235   }
236 }
237
238 Platform::DataCache* ResourceThreadText::CreateDataCache( const std::string& fileName )
239 {
240   // low quality text thread just reads from the cache
241   Platform::DataCache::ReadWriteMode readWriteMode( Platform::DataCache::READ_ONLY);
242
243   // allow high quality distance fields to be written to the cache
244   if( mQuality == TextResourceType::TextQualityHigh )
245   {
246     readWriteMode = Platform::DataCache::READ_WRITE;
247   }
248
249   return Platform::DataCache::New( readWriteMode,
250                                           Platform::DataCache::RUN_LENGTH_ENCODING,
251                                           fileName,
252                                           DISTANCE_FIELD_SIZE,
253                                           MAX_NUMBER_CHARS_TO_CACHE);
254 }
255
256 Platform::DataCache* ResourceThreadText::GetDataCache( const std::string& fileName )
257 {
258   boost::hash<const std::string> hasher;
259   std::size_t hashValue = hasher( fileName );
260
261   // most applications use under 3 fonts, so a simple vector to do the lookup is fine
262   for( unsigned int i = 0; i< mDataCaches.size() ; i++ )
263   {
264     if( mDataCaches[i].second == hashValue)
265     {
266       return mDataCaches[i].first;
267     }
268   }
269   // not found in lookup, create a new data cache
270
271   Platform::DataCache* newCache = CreateDataCache( fileName );
272   mDataCaches.push_back( TDataCachePair( newCache, hashValue ) );
273   return newCache;
274 }
275
276 void ResourceThreadText::DeleteDataCaches()
277 {
278   for( unsigned int i = 0; i< mDataCaches.size() ; i++ )
279   {
280     delete mDataCaches[i].first;
281   }
282   mDataCaches.clear();
283 }
284
285 } // namespace SlpPlatform
286 } // namespace Dali