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