Layout implementation
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
1 /*
2  * Copyright (c) 2015 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-toolkit/internal/text/layouts/layout-engine.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/math/vector2.h>
23 #include <dali/public-api/text-abstraction/font-client.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
27
28 namespace Dali
29 {
30
31 namespace Toolkit
32 {
33
34 namespace Text
35 {
36
37 /**
38  * @brief Stores temporary layout info of the line.
39  */
40 struct LineLayout
41 {
42   GlyphIndex     glyphIndex;         ///< Index of the first glyph to be laid-out.
43   CharacterIndex characterIndex;     ///< Index of the first character to be laid-out.
44   Length         numberOfCharacters; ///< The number of characters which fit in one line.
45   Length         numberOfGlyphs;     ///< The number of glyph which fit in one line.
46   float          length;             ///< The length of the glyphs which fit in one line.
47   float          wsLengthEndOfLine;  ///< The length of the white spaces at the end of the line.
48   float          height;             ///< The maximum height of all fonts in the line.
49   float          ascender;           ///< The maximum ascender of all fonts in the line.
50 };
51
52 struct LayoutEngine::Impl
53 {
54   Impl()
55   : mLayout( LayoutEngine::SINGLE_LINE_BOX )
56   {
57     mFontClient = TextAbstraction::FontClient::Get();
58   }
59
60   /**
61    * Retrieves the line layout for a given box width.
62    */
63   void GetLineLayoutForBox( const LayoutParameters& parameters,
64                             LineLayout& lineLayout )
65   {
66     // Initializes the line layout.
67     lineLayout.numberOfCharacters = 0u;
68     lineLayout.numberOfGlyphs = 0u;
69     lineLayout.length = 0.f;
70     lineLayout.wsLengthEndOfLine = 0.f;
71     lineLayout.height = 0.f;
72     lineLayout.ascender = 0.f;
73
74     // Get the last glyph index.
75     const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
76
77     FontId lastFontId = 0u;
78     for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
79          glyphIndex < parameters.totalNumberOfGlyphs;
80          ++glyphIndex )
81     {
82       // Get the glyph info.
83       const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
84
85       // Get the character indices for the current glyph. The last character index is needed
86       // because there are glyphs formed by more than one character but their break info is
87       // given only for the last character.
88       const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
89       const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
90       const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
91
92       // Get the line break info for the current character.
93       const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
94
95       // Get the word break info for the current character.
96       const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
97
98       // Increase the number of characters.
99       lineLayout.numberOfCharacters += charactersPerGlyph;
100
101       // Increase the number of glyphs.
102       lineLayout.numberOfGlyphs++;
103
104       // Increase the accumulated length.
105       lineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
106
107       if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
108       {
109       }
110       if( TextAbstraction::WORD_BREAK == wordBreakInfo )
111       {
112       }
113
114       if( lastFontId != glyphInfo.fontId )
115       {
116         Text::FontMetrics fontMetrics;
117         mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
118
119         // Sets the maximum height.
120         if( fontMetrics.height > lineLayout.height )
121         {
122           lineLayout.height = fontMetrics.height;
123         }
124
125         // Sets the maximum ascender.
126         if( fontMetrics.ascender > lineLayout.ascender )
127         {
128           lineLayout.ascender = fontMetrics.ascender;
129         }
130
131         lastFontId = glyphInfo.fontId;
132       }
133     }
134   }
135
136   /**
137    * Retrieves the line layout for a given box width.
138    */
139   void GetMultiLineLayoutForBox( const LayoutParameters& parameters,
140                                  LineLayout& lineLayout )
141   {
142     // Initializes the line layout.
143     lineLayout.numberOfCharacters = 0u;
144     lineLayout.numberOfGlyphs = 0u;
145     lineLayout.length = 0.f;
146     lineLayout.wsLengthEndOfLine = 0.f;
147     lineLayout.height = 0.f;
148     lineLayout.ascender = 0.f;
149
150     // Stores temporary line layout which has not been added to the final line layout.
151     LineLayout tmpLineLayout;
152     tmpLineLayout.numberOfCharacters = 0u;
153     tmpLineLayout.numberOfGlyphs = 0u;
154     tmpLineLayout.length = 0.f;
155     tmpLineLayout.wsLengthEndOfLine = 0.f;
156     tmpLineLayout.height = 0.f;
157     tmpLineLayout.ascender = 0.f;
158
159     // Get the last glyph index.
160     const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
161
162     FontId lastFontId = 0u;
163     for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
164          glyphIndex < parameters.totalNumberOfGlyphs;
165          ++glyphIndex )
166     {
167       // Get the glyph info.
168       const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
169
170       // Get the character indices for the current glyph. The last character index is needed
171       // because there are glyphs formed by more than one character but their break info is
172       // given only for the last character.
173       const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
174       const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
175       const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
176
177       // Get the line break info for the current character.
178       const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
179
180       // Get the word break info for the current character.
181       const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
182
183       // Increase the number of characters.
184       tmpLineLayout.numberOfCharacters += charactersPerGlyph;
185
186       // Increase the number of glyphs.
187       tmpLineLayout.numberOfGlyphs++;
188
189       // Increase the accumulated length.
190       tmpLineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
191
192       // Check if the accumulated length fits in the width of the box.
193       if( lineLayout.length + tmpLineLayout.length > parameters.boundingBox.width )
194       {
195         // Current word does not fit in the box's width.
196         return;
197       }
198
199       if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
200       {
201         if( glyphIndex == lastGlyphIndex )
202         {
203           // Must break the line. Update the line layout and return.
204           lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
205           lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
206           lineLayout.length += tmpLineLayout.length;
207           lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
208
209           if( tmpLineLayout.height > lineLayout.height )
210           {
211             lineLayout.height = tmpLineLayout.height;
212           }
213
214           if( tmpLineLayout.ascender > lineLayout.ascender )
215           {
216             lineLayout.ascender = tmpLineLayout.ascender;
217           }
218         }
219
220         tmpLineLayout.numberOfCharacters = 0u;
221         tmpLineLayout.numberOfGlyphs = 0u;
222         tmpLineLayout.length = 0u;
223         tmpLineLayout.wsLengthEndOfLine = 0u;
224         tmpLineLayout.height = 0.f;
225         tmpLineLayout.ascender = 0.f;
226         return;
227       }
228
229       if( TextAbstraction::WORD_BREAK == wordBreakInfo )
230       {
231         // Current glyph is the last one of the current word.
232         // Add the temporal layout to the current one.
233         lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
234         lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
235         lineLayout.length += tmpLineLayout.length;
236         lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
237
238         if( tmpLineLayout.height > lineLayout.height )
239         {
240           lineLayout.height = tmpLineLayout.height;
241         }
242
243         if( tmpLineLayout.ascender > lineLayout.ascender )
244         {
245           lineLayout.ascender = tmpLineLayout.ascender;
246         }
247
248         tmpLineLayout.numberOfCharacters = 0u;
249         tmpLineLayout.numberOfGlyphs = 0u;
250         tmpLineLayout.length = 0u;
251         tmpLineLayout.wsLengthEndOfLine = 0u;
252         tmpLineLayout.height = 0.f;
253         tmpLineLayout.ascender = 0.f;
254       }
255
256       if( lastFontId != glyphInfo.fontId )
257       {
258         Text::FontMetrics fontMetrics;
259         mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
260
261         // Sets the maximum height.
262         if( fontMetrics.height > tmpLineLayout.height )
263         {
264           tmpLineLayout.height = fontMetrics.height;
265         }
266
267         // Sets the maximum ascender.
268         if( fontMetrics.ascender > tmpLineLayout.ascender )
269         {
270           tmpLineLayout.ascender = fontMetrics.ascender;
271         }
272
273         lastFontId = glyphInfo.fontId;
274       }
275     }
276   }
277
278   bool LayoutText( const LayoutParameters& layoutParameters,
279                    Vector<Vector2>& glyphPositions,
280                    Size& actualSize )
281   {
282     // TODO Switch between different layouts
283     bool update = false;
284
285     switch( mLayout )
286     {
287       case LayoutEngine::SINGLE_LINE_BOX:
288       {
289         update = SingleLineLayout( layoutParameters,
290                                    glyphPositions,
291                                    actualSize );
292         break;
293       }
294       case LayoutEngine::MULTI_LINE_BOX:
295       {
296         update = MultiLineLayout( layoutParameters,
297                                   glyphPositions,
298                                   actualSize );
299         break;
300       }
301       default:
302         break;
303     }
304
305     return update;
306   }
307
308   // TODO - Rewrite this to handle bidi
309   bool SingleLineLayout( const LayoutParameters& layoutParameters,
310                          Vector<Vector2>& glyphPositions,
311                          Size& actualSize )
312   {
313     LineLayout layout;
314     layout.glyphIndex = 0u;
315     GetLineLayoutForBox( layoutParameters,
316                          layout );
317
318     if( 0u == layout.numberOfGlyphs )
319     {
320       // The width is too small and no characters are laid-out.
321       return false;
322     }
323
324     // Update the actual size.
325     actualSize.width = layout.length;
326     actualSize.height = layout.height;
327
328     float penX = 0.f;
329     float penY = layout.height;
330
331     Vector2* glyphPositionsBuffer = glyphPositions.Begin();
332     for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
333     {
334       const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
335       Vector2& position = *( glyphPositionsBuffer + glyphIndex );
336
337       position.x = penX + glyph.xBearing;
338       position.y = penY - glyph.yBearing;
339
340       penX += glyph.advance;
341     }
342
343     return true;
344   }
345
346   // TODO - Rewrite this to handle bidi
347   bool MultiLineLayout( const LayoutParameters& layoutParameters,
348                         Vector<Vector2>& glyphPositions,
349                         Size& actualSize )
350   {
351     float penY = 0.f;
352     for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
353     {
354       float penX = 0.f;
355
356       // Get the layout for the line.
357       LineLayout layout;
358       layout.glyphIndex = index;
359       GetMultiLineLayoutForBox( layoutParameters,
360                                 layout );
361
362       if( 0u == layout.numberOfGlyphs )
363       {
364         // The width is too small and no characters are laid-out.
365         return false;
366       }
367
368       // Update the actual size.
369       if( layout.length > actualSize.width )
370       {
371         actualSize.width = layout.length;
372       }
373
374       actualSize.height += layout.height;
375
376       // Traverse the glyphs and set the positions.
377
378       penY += layout.height;
379
380       Vector2* glyphPositionsBuffer = glyphPositions.Begin();
381       for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
382       {
383         const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
384         Vector2& position = *( glyphPositionsBuffer + i );
385
386         position.x = penX + glyph.xBearing;
387         position.y = penY - glyph.yBearing;
388
389         penX += glyph.advance;
390       }
391
392       // Increase the glyph index.
393       index += layout.numberOfGlyphs;
394     }
395
396     return true;
397   }
398
399   LayoutEngine::Layout mLayout;
400
401   TextAbstraction::FontClient mFontClient;
402 };
403
404 LayoutEngine::LayoutEngine()
405 : mImpl( NULL )
406 {
407   mImpl = new LayoutEngine::Impl();
408 }
409
410 LayoutEngine::~LayoutEngine()
411 {
412   delete mImpl;
413 }
414
415 void LayoutEngine::SetLayout( Layout layout )
416 {
417   mImpl->mLayout = layout;
418 }
419
420 unsigned int LayoutEngine::GetLayout() const
421 {
422   return mImpl->mLayout;
423 }
424
425 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
426                                Vector<Vector2>& glyphPositions,
427                                Size& actualSize )
428 {
429   return mImpl->LayoutText( layoutParameters,
430                             glyphPositions,
431                             actualSize );
432 }
433
434 } // namespace Text
435
436 } // namespace Toolkit
437
438 } // namespace Dali