Replaces TextArray type definition.
[platform/core/uifw/dali-core.git] / dali / internal / event / text / atlas / glyph-atlas-manager.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 <dali/internal/event/text/atlas/glyph-atlas-manager.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/internal/event/text/atlas/atlas-ranking.h>
23 #include <dali/internal/event/text/atlas/atlas-size.h>
24 #include <dali/internal/event/text/atlas/debug/atlas-debug.h>
25 #include <dali/integration-api/resource-declarations.h>
26
27 namespace Dali
28 {
29
30 namespace Internal
31 {
32
33
34 GlyphAtlasManager::GlyphAtlasManager( const FontLookupInterface& fontLookup )
35 : mGlyphResourceManager( fontLookup ),
36   mAtlasesChanged(false)
37 {
38 }
39
40 GlyphAtlasManager::~GlyphAtlasManager()
41 {
42   // Atlases automatically get destroyed.
43 }
44
45 TextVertexBuffer* GlyphAtlasManager::TextRequired( const Integration::TextArray& text ,
46                                                    const TextFormat& format,
47                                                    FontMetricsInterface& metrics )
48 {
49   // get the font id
50   FontId fontId = metrics.GetFontId();
51
52   AtlasRanking bestRank( text.Count() );
53
54   // find the atlas which is best suited to displaying the text string
55   GlyphAtlas* atlas  = FindAtlas( text, format, fontId, bestRank);
56
57   DALI_ASSERT_DEBUG( atlas && "Find atlas should always return a valid atlas." );
58
59   // if the atlas is full, create a new larger one
60   if( bestRank.GetSpaceStatus() == AtlasRanking::FULL_CAN_BE_RESIZED )
61   {
62     atlas = CreateLargerAtlas( atlas );
63   }
64
65   // assign the text to it
66   return atlas->AssignText( text, format, fontId, metrics );
67 }
68
69 void GlyphAtlasManager::TextNotRequired( const Integration::TextArray& text,
70                                          const TextFormat& format,
71                                          FontId fontId,
72                                          unsigned int textureId )
73 {
74   GlyphAtlas& atlas = GetAtlas( textureId );
75
76   atlas.TextNoLongerUsed( text, format, fontId  );
77 }
78
79
80 bool GlyphAtlasManager::IsTextLoaded( const Integration::TextArray& text,
81                                       const TextFormat& format,
82                                       FontId fontId,
83                                       unsigned int textureId ) const
84 {
85
86   GlyphAtlas& atlas = GetAtlas( textureId );
87
88   return atlas.IsTextLoaded( text, format, fontId );
89 }
90
91 void GlyphAtlasManager::AddTextObserver( TextObserver& observer)
92 {
93   mGlyphResourceManager.AddTextObserver( observer );
94 }
95
96 void GlyphAtlasManager::RemoveTextObserver( TextObserver& observer)
97 {
98   mGlyphResourceManager.RemoveTextObserver( observer );
99 }
100
101 void GlyphAtlasManager::AddTextureObserver( GlyphTextureObserver& observer)
102 {
103   DALI_ASSERT_DEBUG( std::find(mTextureObservers.Begin(), mTextureObservers.End(), &observer) == mTextureObservers.End() && "Observer already exists" );
104   mTextureObservers.PushBack(&observer);
105 }
106
107 void GlyphAtlasManager::RemoveTextureObserver( GlyphTextureObserver& observer)
108 {
109   TextureObserverList::Iterator iter = std::find(mTextureObservers.Begin(), mTextureObservers.End(), &observer);
110
111   DALI_ASSERT_DEBUG( iter != mTextureObservers.End() && "Observer missing" );
112   mTextureObservers.Erase(iter);
113 }
114
115 void GlyphAtlasManager::SendTextRequests()
116 {
117   if( mAtlasesChanged )
118   {
119     NotifyAtlasObservers();
120     mAtlasesChanged = false;
121   }
122
123   // this is called at the end of an event cycle.
124   // Each atlas builds up a list of text load requests
125   // We grab the requests here and pass them on to glyph-resource-manager
126   for( std::size_t i = 0, atlasCount = mAtlasList.Size() ; i < atlasCount ; ++i )
127   {
128     GlyphAtlas& atlas( *mAtlasList[i] );
129
130     if( atlas.HasPendingRequests() )
131     {
132       const GlyphRequestList& requestList( atlas.GetRequestList() );
133
134       mGlyphResourceManager.AddRequests( requestList, atlas, atlas.GetTextureId() );
135
136       atlas.ClearRequestLists();
137     }
138   }
139 }
140
141 GlyphLoadObserver& GlyphAtlasManager::GetLoadObserver()
142 {
143   return mGlyphResourceManager;
144 }
145
146 GlyphAtlas* GlyphAtlasManager::CreateAtlas( unsigned int size )
147 {
148   GlyphAtlas* atlas =  GlyphAtlas::New( size  );
149
150   AddAtlas( atlas );
151
152   return atlas;
153 }
154
155 GlyphAtlas* GlyphAtlasManager::FindAtlas( const Integration::TextArray& text,
156                                           const TextFormat& format,
157                                           FontId fontId,
158                                           AtlasRanking &bestRank )
159 {
160   // if the text is underlined, add the underline character to the text string
161   Integration::TextArray searchText( text );
162   if( format.IsUnderLined() )
163   {
164     searchText.PushBack( format.GetUnderLineCharacter() );
165   }
166
167   if( mAtlasList.Count() == 0 )
168   {
169     // make sure the initial atlas size holds the requested text.
170     unsigned int size = GlyphAtlasSize::GetInitialSize( searchText.Count() );
171
172     return CreateAtlas( size );
173   }
174
175   // go through each atlas finding the best match
176   GlyphAtlas* bestMatch( NULL );
177
178   for( std::size_t i = 0, atlasCount = mAtlasList.Size() ; i < atlasCount ; ++i )
179   {
180     GlyphAtlas& atlas( *mAtlasList[i] );
181
182     AtlasRanking rank =  atlas.GetRanking( searchText , fontId );
183
184     if( bestRank.HigherRanked( rank ) == false)
185     {
186       bestMatch = &atlas;
187       bestRank = rank;
188     }
189
190     if( rank.AllCharactersMatched() )
191     {
192       // break if an atlas is found which has all the glyphs loaded
193       break;
194     }
195   }
196   return bestMatch;
197 }
198
199 void GlyphAtlasManager::AddAtlas( GlyphAtlas* atlas)
200 {
201   // create a texture for the atlas.
202   unsigned int textureID = mGlyphResourceManager.CreateTexture( atlas->GetSize() );
203
204   // assign the texture id
205   atlas->SetTextureId( textureID );
206
207   mAtlasList.PushBack( atlas );
208
209   // resource manager will inform the atlas when glyphs are loaded or uploaded to a texture
210   mGlyphResourceManager.AddObserver( *atlas );
211 }
212
213 void GlyphAtlasManager::RemoveAtlas( GlyphAtlas* atlas  )
214 {
215   for( std::size_t i = 0; i< mAtlasList.Size(); ++i )
216   {
217     if( mAtlasList[i] == atlas)
218     {
219       // remove it from the resource manager observer list
220       mGlyphResourceManager.RemoveObserver( *atlas );
221
222       // remove the item from the list & delete it
223       mAtlasList.Erase( mAtlasList.Begin()+i );
224       return;
225     }
226   }
227   DALI_ASSERT_DEBUG( 0 && "atlas not found");
228 }
229
230 GlyphAtlas& GlyphAtlasManager::GetAtlas( unsigned int textureId ) const
231 {
232   for( std::size_t i = 0, atlasCount = mAtlasList.Size() ; i < atlasCount ; ++i )
233   {
234     GlyphAtlas& atlas( *mAtlasList[i] );
235
236     if( atlas.GetTextureId() == textureId )
237     {
238       return atlas;
239     }
240
241     // check if the texture id is for an old atlas that has been replaced by this atlas
242     if( atlas.HasReplacedTexture( textureId ) )
243     {
244       return atlas;
245     }
246   }
247   DALI_ASSERT_ALWAYS( 0 && "Atlas not found");
248 }
249
250 GlyphAtlas* GlyphAtlasManager::CreateLargerAtlas( GlyphAtlas* atlas )
251 {
252   if( atlas->GetSize() ==  GlyphAtlasSize::GetMaxSize() )
253   {
254     // @todo implement atlas splitting
255     DALI_ASSERT_ALWAYS(0 && "Atlas reached max size");
256   }
257
258   // Create a new bigger atlas
259   unsigned int biggerSize = GlyphAtlasSize::GetNextSize( atlas->GetSize() );
260
261   GlyphAtlas* newAtlas = GlyphAtlas::New( biggerSize );
262
263   // clone the contents of the old atlas
264   newAtlas->CloneContents( atlas );
265
266   // remove the old atlas
267   RemoveAtlas( atlas );
268
269   // add the new atlas
270   AddAtlas( newAtlas );
271
272   mAtlasesChanged = true;
273
274   return newAtlas;
275 }
276
277 void GlyphAtlasManager::NotifyAtlasObservers()
278 {
279   DALI_LOG_INFO(gTextAtlasLogFilter, Debug::General, "GlyphAtlasManager::NotifyAtlasObservers()\n");
280
281   for( std::size_t i = 0, atlasCount = mAtlasList.Size() ; i < atlasCount ; ++i )
282   {
283     GlyphAtlas& atlas( *mAtlasList[i] );
284
285     Integration::ResourceId newTexture;
286     TextureIdList oldTextures;
287     atlas.GetNewTextureId( oldTextures, newTexture );
288
289     // copy this list so, the observers can remove themselves during the call back
290     TextureObserverList observerList( mTextureObservers );
291
292     TextureObserverList::Iterator iter( observerList.Begin() );
293     TextureObserverList::ConstIterator endIter( observerList.End() );
294     for( ; iter != endIter; ++iter )
295     {
296       GlyphTextureObserver* observer((*iter));
297       observer->TextureResized( oldTextures, newTexture );
298     }
299   }
300 }
301
302 } // namespace Internal
303
304 } // namespace Dali