2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "resource-thread-text.h"
22 #include "loader-font.h"
23 #include "../interfaces/data-cache.h"
24 #include "../data-cache/tests/data-cache-debug.h"
27 #include <boost/functional/hash.hpp>
30 #include FT_FREETYPE_H
33 #include <dali/integration-api/glyph-set.h>
34 #include <dali/integration-api/platform-abstraction.h>
37 using namespace Dali::Integration;
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
53 boost::mutex mFileCheckMutex; ///< used to ensure only one thread checks the files for corruption on startup
56 }; // unnamed namespace
58 ResourceThreadText::ResourceThreadText(ResourceLoader& resourceLoader, const TextResourceType::TextQuality quality)
59 : ResourceThreadBase(resourceLoader),
61 mFreeTypeHandle( NULL )
64 #ifdef DATA_CACHE_DEBUG
65 DataCacheIo::ThreadedStressTest();
69 ResourceThreadText::~ResourceThreadText()
77 FT_Done_FreeType(mFreeTypeHandle);
81 void ResourceThreadText::Load(const ResourceRequest& request)
83 // 1: Check if glyphs are in the data cache
84 // 2: For any missing glyphs, load from FreeType and save to cache file.
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'.
89 // This means we never call mResourceLoader.AddFailedLoad( ...)
91 const TextResourceType& textRequest = dynamic_cast<const TextResourceType&>(*request.GetType());
92 TextResourceType::CharacterList text(textRequest.mCharacterList);
94 std::string cachePath(DALI_USER_FONT_CACHE_PATH);
95 cachePath.append(request.GetPath() + "-" + textRequest.mStyle);
96 std::replace(cachePath.begin(), cachePath.end(), ' ', '-');
98 Platform::DataCache* dataCache = GetDataCache( cachePath );
100 GlyphSet *glyphSet( new GlyphSet() );
101 glyphSet->mFontHash = textRequest.mFontHash;
102 glyphSet->SetAtlasResourceId(textRequest.mTextureAtlasId);
104 if( textRequest.mCache == TextResourceType::GLYPH_CACHE_READ )
106 LoadCharactersFromCache( *dataCache, *glyphSet, text );
110 LoadCharacters( *dataCache, *glyphSet, textRequest, request.GetPath(), text );
113 IntrusivePtr<GlyphSet> glyphResource( glyphSet );
114 LoadedResource resource( request.GetId(), request.GetType()->id, glyphResource );
115 mResourceLoader.AddPartiallyLoadedResource(resource);
118 void ResourceThreadText::Save(const ResourceRequest& request)
122 void ResourceThreadText::LoadCharactersFromCache(
123 Platform::DataCache& dataCache,
125 const TextResourceType::CharacterList& requestedCharacters )
127 Platform::DataCache::KeyVector keyVector;
128 for( std::size_t i=0, length=requestedCharacters.size(); i<length; i++ )
130 keyVector.push_back(requestedCharacters[i].character);
132 Platform::DataCache::DataVector dataVector;
134 // load the glyphs from file
135 dataCache.Find( keyVector, dataVector );
138 // for each glyph found, add to the glyph set
139 for( std::size_t n = 0, arraySize = keyVector.size(); n < arraySize ; n++ )
141 Platform::DataCache::Data& data( dataVector[n]);
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;
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 );
156 bitmapData->GetPackedPixelsProfile()->AssignBuffer( Pixel::A8, data.data, DISTANCE_FIELD_SIZE, DISTANCE_FIELD_WIDTH, DISTANCE_FIELD_HEIGHT );
160 // add to the glyphset
161 glyphSet.AddCharacter( bitmapData, glyphMetrics );
166 void ResourceThreadText::SaveCharacters( Platform::DataCache& dataCache, const GlyphSet& glyphSet )
168 const GlyphSet::CharacterList& chars = glyphSet.GetCharacterList();
170 Platform::DataCache::KeyVector keyVector;
171 Platform::DataCache::DataVector dataVector;
173 keyVector.reserve( chars.size() );
174 dataVector.reserve( chars.size() );
176 for( GlyphSet::CharacterList::const_iterator it = chars.begin(), endIt = chars.end(); it != endIt; ++it )
178 const BitmapPtr& bitmap( it->first );
181 const GlyphMetrics& metrics( it->second );
183 Platform::DataCache::DataKey key( metrics.code );
184 Platform::DataCache::Data data;
185 data.SetData( bitmap->GetBuffer(), DISTANCE_FIELD_SIZE );
187 keyVector.push_back( key );
188 dataVector.push_back( data );
192 // this will save the distance fields to file
193 dataCache.Add( keyVector, dataVector );
197 void ResourceThreadText::LoadCharacters( Platform::DataCache& dataCache,
199 const TextResourceType& textRequest,
200 const std::string& path,
201 const TextResourceType::CharacterList& charCodes )
203 // we need to use freetype, ensure the library has been initialised
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);
209 const GlyphSet::CharacterList& cachedChars = missingGlyphSet->GetCharacterList();
210 for( GlyphSet::CharacterList::const_iterator it = cachedChars.begin(), endIt = cachedChars.end(); it != endIt; ++it )
212 glyphSet.AddCharacter( *it );
215 // save to cache if high quality
216 if( ( mQuality == TextResourceType::TextQualityHigh ) && ( TextResourceType::GLYPH_CACHE_WRITE == textRequest.mCache ) )
218 SaveCharacters( dataCache, glyphSet );
221 // all the information in missingGlyphSet has been copied, so delete it.
222 delete missingGlyphSet;
225 void ResourceThreadText::LoadFreeType()
227 if( mFreeTypeHandle )
232 if( FT_Init_FreeType( &mFreeTypeHandle ) != 0 )
234 DALI_ASSERT_ALWAYS( 0 && "FT_Init_FreeType failed\n");
238 Platform::DataCache* ResourceThreadText::CreateDataCache( const std::string& fileName )
240 // low quality text thread just reads from the cache
241 Platform::DataCache::ReadWriteMode readWriteMode( Platform::DataCache::READ_ONLY);
243 // allow high quality distance fields to be written to the cache
244 if( mQuality == TextResourceType::TextQualityHigh )
246 readWriteMode = Platform::DataCache::READ_WRITE;
249 return Platform::DataCache::New( readWriteMode,
250 Platform::DataCache::RUN_LENGTH_ENCODING,
253 MAX_NUMBER_CHARS_TO_CACHE);
256 Platform::DataCache* ResourceThreadText::GetDataCache( const std::string& fileName )
258 boost::hash<const std::string> hasher;
259 std::size_t hashValue = hasher( fileName );
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++ )
264 if( mDataCaches[i].second == hashValue)
266 return mDataCaches[i].first;
269 // not found in lookup, create a new data cache
271 Platform::DataCache* newCache = CreateDataCache( fileName );
272 mDataCaches.push_back( TDataCachePair( newCache, hashValue ) );
276 void ResourceThreadText::DeleteDataCaches()
278 for( unsigned int i = 0; i< mDataCaches.size() ; i++ )
280 delete mDataCaches[i].first;
285 } // namespace SlpPlatform