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.
20 #include "glyph-resource-manager.h"
23 #include <dali/integration-api/debug.h>
24 #include <dali/integration-api/platform-abstraction.h>
25 #include <dali/internal/event/common/thread-local-storage.h>
26 #include <dali/internal/event/resources/resource-client.h>
27 #include <dali/public-api/images/pixel.h>
28 #include <dali/internal/event/text/resource/debug/glyph-resource-debug.h>
30 using namespace Dali::Integration;
41 * Convert the quality level into a loaded status
43 GlyphResourceObserver::Quality GetGlyphStatus( unsigned int quality )
45 if( quality == Integration::GlyphMetrics::LOW_QUALITY)
47 return GlyphResourceObserver::LOW_QUALITY_LOADED;
51 return GlyphResourceObserver::HIGH_QUALITY_LOADED;
54 } // un-named namespace
56 GlyphResourceManager::GlyphResourceManager( const FontLookupInterface& fontLookup )
57 :mFontLookup( fontLookup ),
58 mResourceClient( ThreadLocalStorage::Get().GetResourceClient() )
62 GlyphResourceManager::~GlyphResourceManager()
67 unsigned int GlyphResourceManager::CreateTexture(unsigned int size )
69 // create a new texture. Using Alpha 8 = 1 byte per pixel
70 ResourceTicketPtr ticket = mResourceClient.AllocateTexture( size, size, Pixel::A8 );
72 mTextureTickets.push_back( ticket );
74 // return the texture id
75 return ticket->GetId();
78 void GlyphResourceManager::AddObserver( GlyphResourceObserver& observer)
80 DALI_ASSERT_DEBUG( ( mObservers.find( &observer ) == mObservers.end() ) && "Observer already exists");
81 mObservers.insert( &observer );
84 void GlyphResourceManager::RemoveObserver( GlyphResourceObserver& observer)
86 DALI_ASSERT_DEBUG( ( mObservers.find( &observer ) != mObservers.end() ) && "Observer not found");
87 mObservers.erase( &observer );
90 void GlyphResourceManager::AddTextObserver( TextObserver& observer)
92 DALI_ASSERT_DEBUG( ( mTextObservers.find( &observer ) == mTextObservers.end() ) && "Observer already exists");
93 mTextObservers.insert( &observer );
96 void GlyphResourceManager::RemoveTextObserver( TextObserver& observer)
98 DALI_ASSERT_DEBUG( ( mTextObservers.find( &observer ) != mTextObservers.end() ) && "Observer doesn't exists");
99 mTextObservers.erase( &observer );
103 void GlyphResourceManager::AddRequests( const GlyphRequestList& requestList,
104 GlyphResourceObserver& observer,
105 ResourceId atlasTextureId)
107 // each entry in the request list is for a specific font,
108 // style, quality and a list of characters
110 for( std::size_t n = 0, size = requestList.size(); n < size ; ++n )
112 const GlyphResourceRequest& request( requestList[n] );
113 SendRequests( request, observer, atlasTextureId );
117 void GlyphResourceManager::GlyphsLoaded( Integration::ResourceId id, const Integration::GlyphSet& glyphSet, LoadStatus loadStatus )
119 // Get the the observer
120 GlyphResourceObserver* observer = GetObserver( id );
122 DALI_LOG_INFO( gTextLogFilter, Debug::General,
123 "GlyphResourceManager::GlyphsLoaded: id:%d, status:%s textureId:%d observer:%p\n",
125 loadStatus==RESOURCE_LOADING?"LOADING":loadStatus==RESOURCE_PARTIALLY_LOADED?"PARTIAL":"COMPLETED",
126 glyphSet.GetAtlasResourceId(),
129 DALI_LOG_INFO( gTextLogFilter, Debug::Verbose, "GlyphResourceManager::GlyphsLoaded: %s\n", DebugCharacterString(glyphSet).c_str() );
133 FontId fontId = glyphSet.mFontHash;
135 // Stage 1. Inform the observer of the characters that have been loaded
136 UpdateObserver( observer, fontId, glyphSet, GLYPH_LOADED_FROM_FILE );
138 // Stage 2. Tell the observers the glyphs have been uploaded to GL (Resource manager is
139 // responsible for this now)
141 // @todo If there is an issue with the timing of the text-loaded signal from text-actor
142 // we can set this status after the upload instead of before (using uploaded callback on texture ticket).
143 UpdateObserver( observer, fontId, glyphSet, GLYPH_UPLOADED_TO_GL );
145 // Stage 3. Tell the text-observers some text has been loaded.
146 // They can then query if the text they are using has been uploaded
147 NotifyTextObservers();
149 // Only remove the ticket when all the responses have been received
150 if( loadStatus == RESOURCE_COMPLETELY_LOADED )
152 mGlyphLoadTickets.erase( id );
157 // The observer has been deleted after the resource request was sent.
158 // Note, we may still get responses that are already in the system - in this case,
160 mGlyphLoadTickets.erase( id );
164 void GlyphResourceManager::SendRequests( const GlyphResourceRequest& request, GlyphResourceObserver& observer, ResourceId atlasTextureId )
166 Integration::PlatformAbstraction& platform = Internal::ThreadLocalStorage::Get().GetPlatformAbstraction();
168 // get the font information from the lookup, using the font id
169 std::string family, style;
170 float maxGlyphWidth, maxGlyphHeight;
171 FontId fontId = request.GetFontId();
172 mFontLookup.GetFontInformation( fontId, family, style, maxGlyphWidth, maxGlyphHeight );
174 // List of requested characters
175 const Integration::TextResourceType::CharacterList& requestedCharacters = request.GetCharacterList();
176 const size_t requestedCharacterCount = requestedCharacters.size();
178 DALI_LOG_INFO( gTextLogFilter, Debug::Verbose, "GlyphResourceManager::SendRequests() - requested character list: %s\n",
179 DebugCharacterString(requestedCharacters).c_str() );
181 // create a new resource request for the characters
182 Integration::TextResourceType resourceType( fontId, style, requestedCharacters, atlasTextureId,
183 Integration::TextResourceType::TextQualityHigh, // TODO: Remove
184 Vector2 (maxGlyphWidth, maxGlyphHeight),
185 Integration::TextResourceType::GLYPH_CACHE_WRITE );
187 // Try to synchronously load cached versions of the glyph bitmaps
188 Integration::GlyphSetPointer cachedGlyphs = platform.GetCachedGlyphData( resourceType, family );
189 const GlyphSet::CharacterList& cachedCharacters = cachedGlyphs->GetCharacterList(); // list of cached glyphs
190 const size_t cachedCharacterCount = cachedCharacters.size(); // number of cached glyphs
192 // Any glyphs loaded from cache?
193 if( 0u != cachedCharacterCount )
195 // yes..Upload cached bitmaps to texture
196 UploadGlyphsToTexture( &observer, fontId, *(cachedGlyphs.Get()) );
197 UpdateObserver( &observer, fontId, *(cachedGlyphs.Get()), GLYPH_UPLOADED_TO_GL );
198 NotifyTextObservers();
201 // Any glyphs still missing?
202 if( requestedCharacterCount != cachedCharacterCount )
204 // create a list of uncached/missing glyphs
205 Integration::TextResourceType::CharacterList uncachedCharacters;
206 for( size_t i = 0; i < requestedCharacterCount; ++i )
208 uint32_t charCode = requestedCharacters[ i ].character;
209 bool isCached = false;
210 for( size_t j = 0; j < cachedCharacterCount; ++j )
212 if( cachedCharacters[ j ].second.code == charCode )
220 uncachedCharacters.push_back( requestedCharacters[ i ] );
224 // replace requested character list with missing character list for resource request
225 resourceType.mCharacterList.assign( uncachedCharacters.begin(), uncachedCharacters.end() );
227 // Make asynchronous request for the missing glyphs
228 ResourceTicketPtr ticket = mResourceClient.RequestResource( resourceType, family );
231 mGlyphLoadTickets[ ticket->GetId() ] = ( ObserverTicketPair(ticket, &observer) );
233 DALI_LOG_INFO( gTextLogFilter, Debug::General, "GlyphResourceManager::SendRequests() - id:%d observer:%p\n",
234 ticket->GetId(), &observer );
236 DALI_LOG_INFO( gTextLogFilter, Debug::Verbose, "GlyphResourceManager::SendRequests() - uncached character list:%s\n",
237 DebugCharacterString(uncachedCharacters).c_str() );
239 // Also synchronously load low quality version of glyphs
240 resourceType.mQuality = Integration::TextResourceType::TextQualityLow;
241 Integration::GlyphSetPointer lowQualityGlyphPointer = platform.GetGlyphData( resourceType, family, true );
242 Integration::GlyphSet& lowQualityGlyphs = *(lowQualityGlyphPointer.Get());
243 size_t lowQualityCharacterCount = lowQualityGlyphs.GetCharacterList().size();
245 // Any low quality glyphs loaded?
246 if( 0u != lowQualityCharacterCount )
248 // yes..Upload cached bitmaps to texture
249 UploadGlyphsToTexture( &observer, fontId, lowQualityGlyphs );
250 // Update atlas load status in update thread
251 mResourceClient.UpdateAtlasStatus( ticket->GetId(), resourceType.mTextureAtlasId, RESOURCE_PARTIALLY_LOADED );
252 // Notify observers and text observers that a partial load has occured
253 GlyphsLoaded( ticket->GetId(), lowQualityGlyphs, RESOURCE_PARTIALLY_LOADED );
258 void GlyphResourceManager::UploadGlyphsToTexture( GlyphResourceObserver* observer,
260 const Integration::GlyphSet& glyphSet )
262 // the glyphset contains an array of bitmap / characters .
263 // The function uploads the bitmaps to a texture
264 const Integration::GlyphSet::CharacterList& charList( glyphSet.GetCharacterList() );
265 BitmapUploadArray uploadArray;
266 for(std::size_t i = 0, count = charList.size() ; i < count; i++ )
268 const Integration::GlyphSet::Character& character( charList[i] );
269 uint32_t charCode = character.second.code;
270 unsigned int xPos,yPos;
271 // ask the observer (atlas) where the bitmap should be uploaded to
272 bool inUse = observer->GetGlyphTexturePosition( charCode, fontId, xPos, yPos );
275 // if it's no longer used, don't upload
278 // grab a pointer to the bitmap
279 Integration::Bitmap* bitmap( charList[ i ].first.Get() );
280 unsigned int bitmapWidth = bitmap->GetImageWidth();
281 unsigned int bitmapHeight = bitmap->GetImageHeight();
282 Bitmap::PackedPixelsProfile* packedBitmap = bitmap->GetPackedPixelsProfile();
283 if( NULL != packedBitmap )
285 bitmapWidth = packedBitmap->GetBufferWidth();
286 bitmapHeight = packedBitmap->GetBufferHeight();
289 // create a bitmap upload object, then add it to the array
290 BitmapUpload upload( bitmap->ReleaseBuffer(), // Inform the bitmap we're taking ownership of it's pixel buffer.
291 xPos, // x position in the texture to upload the bitmap to
292 yPos, // y position in the texture to upload the bitmap to
293 bitmapWidth, // bitmap width
294 bitmapHeight, // bitmap height
295 BitmapUpload::DISCARD_PIXEL_DATA ); // tell the the texture to delete the bitmap pixel buffer when it's done
296 uploadArray.push_back( upload );
298 // update the texture
299 mResourceClient.UpdateTexture( observer->GetTextureId(), uploadArray );
302 void GlyphResourceManager::NotifyTextObservers()
304 // copy this list so, the observers can remove themselves during the call back
305 TextObserverList observerList( mTextObservers );
307 TextObserverList::iterator iter( observerList.begin() );
308 TextObserverList::const_iterator endIter( observerList.end() );
309 for( ; iter != endIter; ++iter )
311 TextObserver* observer((*iter));
312 observer->TextLoaded();
316 void GlyphResourceManager::DeleteOldTextures( GlyphResourceObserver* observer )
318 // see if the observer is doing a texture-resize operation
319 GlyphResourceObserver::TextureState textureState = observer->GetTextureState();
321 if( textureState == GlyphResourceObserver::TEXTURE_RESIZED )
323 unsigned int newTexture;
324 TextureIdList oldTextures;
326 observer->GetNewTextureId( oldTextures, newTexture );
328 // the old texture(s) can be deleted,
329 // this is done automatically when we release the ticket
330 for( std::size_t i = 0; i< oldTextures.size(); i++ )
332 DeleteTextureTicket( oldTextures[i] );
337 void GlyphResourceManager::UpdateObserver( GlyphResourceObserver* observer,
339 const Integration::GlyphSet& glyphSet,
340 GlyphUpdateType updateType)
342 const Integration::GlyphSet::CharacterList& charList( glyphSet.GetCharacterList() );
344 for(std::size_t i = 0, count = charList.size() ; i < count; i++ )
346 const Integration::GlyphSet::Character& character( charList[i] );
348 uint32_t charCode = character.second.code;
349 uint32_t quality = character.second.quality;
351 if( updateType == GLYPH_LOADED_FROM_FILE)
353 observer->GlyphLoadedFromFile( charCode, fontId, GetGlyphStatus( quality ) );
355 else // updateType == GLYPH_UPLOADED_TO_GL
357 observer->GlyphUpLoadedToTexture( charCode, fontId );
361 if( updateType == GLYPH_UPLOADED_TO_GL )
363 DeleteOldTextures(observer);
367 GlyphResourceObserver* GlyphResourceManager::GetObserver( Integration::ResourceId id )
369 GlyphResourceObserver* observer = NULL;
371 // Get the observer for a resource
372 TicketList::iterator iter = mGlyphLoadTickets.find( id );
374 if( iter != mGlyphLoadTickets.end() ) // Only check for observers if the ticket is still alive
376 ObserverTicketPair& observerTicket ( (*iter).second );
377 observer = observerTicket.second;
379 // check if the atlas is still alive and in the observer list
380 if( mObservers.find( observer ) == mObservers.end() )
389 void GlyphResourceManager::DeleteTextureTicket(unsigned int id )
391 TextureTickets::iterator endIter;
393 for( TextureTickets::iterator iter = mTextureTickets.begin(); iter != endIter; ++iter )
395 ResourceTicketPtr ticket = (*iter);
396 if( ticket->GetId() == id )
398 mTextureTickets.erase( iter );
404 Integration::TextResourceType::TextQuality GlyphResourceManager::GetQuality( GlyphResourceRequest::GlyphQuality quality )
406 if( quality == GlyphResourceRequest::LOW_QUALITY)
408 return Integration::TextResourceType::TextQualityLow;
410 return Integration::TextResourceType::TextQualityHigh;
413 } // namespace Internal