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