Layout implementation
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
index ac2c53f..b5c3b16 100644 (file)
@@ -23,8 +23,7 @@
 #include <dali/public-api/text-abstraction/font-client.h>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/internal/text/logical-model.h>
-#include <dali-toolkit/internal/text/visual-model.h>
+#include <dali-toolkit/internal/text/layouts/layout-parameters.h>
 
 namespace Dali
 {
@@ -35,6 +34,21 @@ namespace Toolkit
 namespace Text
 {
 
+/**
+ * @brief Stores temporary layout info of the line.
+ */
+struct LineLayout
+{
+  GlyphIndex     glyphIndex;         ///< Index of the first glyph to be laid-out.
+  CharacterIndex characterIndex;     ///< Index of the first character to be laid-out.
+  Length         numberOfCharacters; ///< The number of characters which fit in one line.
+  Length         numberOfGlyphs;     ///< The number of glyph which fit in one line.
+  float          length;             ///< The length of the glyphs which fit in one line.
+  float          wsLengthEndOfLine;  ///< The length of the white spaces at the end of the line.
+  float          height;             ///< The maximum height of all fonts in the line.
+  float          ascender;           ///< The maximum ascender of all fonts in the line.
+};
+
 struct LayoutEngine::Impl
 {
   Impl()
@@ -43,169 +57,346 @@ struct LayoutEngine::Impl
     mFontClient = TextAbstraction::FontClient::Get();
   }
 
-  void UpdateVisualModel( const Vector2& boundingBox,
-                          const Vector<GlyphInfo>& glyphs,
-                          const Vector<CharacterIndex>& characterIndices,
-                          const Vector<Length>& charactersPerGlyph,
-                          VisualModel& visualModel )
+  /**
+   * Retrieves the line layout for a given box width.
+   */
+  void GetLineLayoutForBox( const LayoutParameters& parameters,
+                            LineLayout& lineLayout )
   {
-    // TODO Switch between different layouts
+    // Initializes the line layout.
+    lineLayout.numberOfCharacters = 0u;
+    lineLayout.numberOfGlyphs = 0u;
+    lineLayout.length = 0.f;
+    lineLayout.wsLengthEndOfLine = 0.f;
+    lineLayout.height = 0.f;
+    lineLayout.ascender = 0.f;
+
+    // Get the last glyph index.
+    const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
+
+    FontId lastFontId = 0u;
+    for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
+         glyphIndex < parameters.totalNumberOfGlyphs;
+         ++glyphIndex )
+    {
+      // Get the glyph info.
+      const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
 
-    visualModel.SetGlyphs( &glyphs[0],
-                           &characterIndices[0],
-                           &charactersPerGlyph[0],
-                           glyphs.Count() );
+      // Get the character indices for the current glyph. The last character index is needed
+      // because there are glyphs formed by more than one character but their break info is
+      // given only for the last character.
+      const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
+      const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
+      const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
 
-    UpdateGlyphPositions( boundingBox, visualModel );
-  }
+      // Get the line break info for the current character.
+      const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
 
-  void UpdateGlyphPositions( const Vector2& boundingBox, VisualModel& visualModel )
-  {
-    if( LayoutEngine::SINGLE_LINE_BOX == mLayout )
-    {
-      SingleLineLayout( boundingBox, visualModel );
-    }
-    else
-    {
-      MultiLineLayout( boundingBox, visualModel );
-    }
-  }
+      // Get the word break info for the current character.
+      const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
 
-  // TODO - Rewrite this to handle bidi
-  void SingleLineLayout( const Vector2& boundingBox, VisualModel& visualModel )
-  {
-    Length glyphCount = visualModel.GetNumberOfGlyphs();
-
-    std::vector<Vector2> glyphPositions;
-    glyphPositions.reserve( glyphCount );
+      // Increase the number of characters.
+      lineLayout.numberOfCharacters += charactersPerGlyph;
 
-    if( glyphCount > 0 )
-    {
-      // FIXME Single font assumption
-      Text::FontMetrics fontMetrics;
-      GlyphInfo firstGlyph;
-      visualModel.GetGlyphs( &firstGlyph, 0, 1 );
-      mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
+      // Increase the number of glyphs.
+      lineLayout.numberOfGlyphs++;
 
-      float penX( 0 );
-      float penY( fontMetrics.ascender ); // Move to baseline
+      // Increase the accumulated length.
+      lineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
 
-      for( unsigned int i=0; i<glyphCount; ++i )
+      if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
+      {
+      }
+      if( TextAbstraction::WORD_BREAK == wordBreakInfo )
       {
-        GlyphInfo glyph;
-        visualModel.GetGlyphs( &glyph, i, 1 );
+      }
 
-        glyphPositions.push_back( Vector2( penX + glyph.xBearing,
-                                           penY - glyph.yBearing ) );
+      if( lastFontId != glyphInfo.fontId )
+      {
+        Text::FontMetrics fontMetrics;
+        mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
 
-        penX += glyph.advance;
-      }
+        // Sets the maximum height.
+        if( fontMetrics.height > lineLayout.height )
+        {
+          lineLayout.height = fontMetrics.height;
+        }
 
-      visualModel.SetGlyphPositions( &glyphPositions[0], glyphCount );
+        // Sets the maximum ascender.
+        if( fontMetrics.ascender > lineLayout.ascender )
+        {
+          lineLayout.ascender = fontMetrics.ascender;
+        }
 
-      visualModel.SetActualSize( Vector2(penX, fontMetrics.height) );
+        lastFontId = glyphInfo.fontId;
+      }
     }
   }
 
-  // TODO - Rewrite this to handle bidi
-  void MultiLineLayout( const Vector2& boundingBox, VisualModel& visualModel )
+  /**
+   * Retrieves the line layout for a given box width.
+   */
+  void GetMultiLineLayoutForBox( const LayoutParameters& parameters,
+                                 LineLayout& lineLayout )
   {
-    Length glyphCount = visualModel.GetNumberOfGlyphs();
+    // Initializes the line layout.
+    lineLayout.numberOfCharacters = 0u;
+    lineLayout.numberOfGlyphs = 0u;
+    lineLayout.length = 0.f;
+    lineLayout.wsLengthEndOfLine = 0.f;
+    lineLayout.height = 0.f;
+    lineLayout.ascender = 0.f;
+
+    // Stores temporary line layout which has not been added to the final line layout.
+    LineLayout tmpLineLayout;
+    tmpLineLayout.numberOfCharacters = 0u;
+    tmpLineLayout.numberOfGlyphs = 0u;
+    tmpLineLayout.length = 0.f;
+    tmpLineLayout.wsLengthEndOfLine = 0.f;
+    tmpLineLayout.height = 0.f;
+    tmpLineLayout.ascender = 0.f;
+
+    // Get the last glyph index.
+    const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
+
+    FontId lastFontId = 0u;
+    for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
+         glyphIndex < parameters.totalNumberOfGlyphs;
+         ++glyphIndex )
+    {
+      // Get the glyph info.
+      const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
 
-    std::vector<Vector2> glyphPositions;
-    glyphPositions.reserve( glyphCount );
+      // Get the character indices for the current glyph. The last character index is needed
+      // because there are glyphs formed by more than one character but their break info is
+      // given only for the last character.
+      const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
+      const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
+      const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
 
-    if( glyphCount > 0 )
-    {
-      Size actualSize;
+      // Get the line break info for the current character.
+      const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
 
-      // FIXME Single font assumption
-      Text::FontMetrics fontMetrics;
-      GlyphInfo firstGlyph;
-      visualModel.GetGlyphs( &firstGlyph, 0, 1 );
-      mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
+      // Get the word break info for the current character.
+      const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
 
-      float penX( 0 );
-      float penY( fontMetrics.ascender ); // Move to baseline
+      // Increase the number of characters.
+      tmpLineLayout.numberOfCharacters += charactersPerGlyph;
 
-      unsigned int i=0;
-      while( i < glyphCount )
+      // Increase the number of glyphs.
+      tmpLineLayout.numberOfGlyphs++;
+
+      // Increase the accumulated length.
+      tmpLineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
+
+      // Check if the accumulated length fits in the width of the box.
+      if( lineLayout.length + tmpLineLayout.length > parameters.boundingBox.width )
+      {
+        // Current word does not fit in the box's width.
+        return;
+      }
+
+      if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
       {
-        // Skip initial whitespace
-        for( ; i<glyphCount; ++i )
+        if( glyphIndex == lastGlyphIndex )
         {
-          GlyphInfo glyph;
-          visualModel.GetGlyphs( &glyph, i, 1 );
+          // Must break the line. Update the line layout and return.
+          lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
+          lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
+          lineLayout.length += tmpLineLayout.length;
+          lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
 
-          if( glyph.width  > 0 &&
-              glyph.height > 0 )
+          if( tmpLineLayout.height > lineLayout.height )
           {
-            break;
+            lineLayout.height = tmpLineLayout.height;
           }
-          else
+
+          if( tmpLineLayout.ascender > lineLayout.ascender )
           {
-            glyphPositions.push_back( Vector2( penX + glyph.xBearing,
-                                               penY - glyph.yBearing ) );
+            lineLayout.ascender = tmpLineLayout.ascender;
           }
         }
 
-        // Find last glyph for the next line
-        unsigned int endIndex = i;
-        float endPenX = penX;
-        unsigned int j=i;
-        for( ; j<glyphCount; ++j )
-        {
-          GlyphInfo glyph;
-          visualModel.GetGlyphs( &glyph, j, 1 );
+        tmpLineLayout.numberOfCharacters = 0u;
+        tmpLineLayout.numberOfGlyphs = 0u;
+        tmpLineLayout.length = 0u;
+        tmpLineLayout.wsLengthEndOfLine = 0u;
+        tmpLineLayout.height = 0.f;
+        tmpLineLayout.ascender = 0.f;
+        return;
+      }
 
-          endPenX += glyph.advance;
+      if( TextAbstraction::WORD_BREAK == wordBreakInfo )
+      {
+        // Current glyph is the last one of the current word.
+        // Add the temporal layout to the current one.
+        lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
+        lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
+        lineLayout.length += tmpLineLayout.length;
+        lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
+
+        if( tmpLineLayout.height > lineLayout.height )
+        {
+          lineLayout.height = tmpLineLayout.height;
+        }
 
-          if( glyph.width  <= 0 ||
-              glyph.height <= 0 )
-          {
-            // Potential line end found
-            endIndex = j;
-          }
-          else if( endPenX > boundingBox.width )
-          {
-            break;
-          }
+        if( tmpLineLayout.ascender > lineLayout.ascender )
+        {
+          lineLayout.ascender = tmpLineLayout.ascender;
         }
 
-        actualSize.width = ( actualSize.width < endPenX ) ? endPenX : actualSize.width;
+        tmpLineLayout.numberOfCharacters = 0u;
+        tmpLineLayout.numberOfGlyphs = 0u;
+        tmpLineLayout.length = 0u;
+        tmpLineLayout.wsLengthEndOfLine = 0u;
+        tmpLineLayout.height = 0.f;
+        tmpLineLayout.ascender = 0.f;
+      }
+
+      if( lastFontId != glyphInfo.fontId )
+      {
+        Text::FontMetrics fontMetrics;
+        mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
 
-        // If end of text or no whitespace found
-        if( glyphCount == j ||
-            endIndex == i )
+        // Sets the maximum height.
+        if( fontMetrics.height > tmpLineLayout.height )
         {
-          endIndex = j;
+          tmpLineLayout.height = fontMetrics.height;
         }
 
-        for( ; i<endIndex; ++i )
+        // Sets the maximum ascender.
+        if( fontMetrics.ascender > tmpLineLayout.ascender )
         {
-          GlyphInfo glyph;
-          visualModel.GetGlyphs( &glyph, i, 1 );
+          tmpLineLayout.ascender = fontMetrics.ascender;
+        }
 
-          glyphPositions.push_back( Vector2( penX + glyph.xBearing,
-                                             penY - glyph.yBearing ) );
+        lastFontId = glyphInfo.fontId;
+      }
+    }
+  }
 
-          penX += glyph.advance;
-        }
+  bool LayoutText( const LayoutParameters& layoutParameters,
+                   Vector<Vector2>& glyphPositions,
+                   Size& actualSize )
+  {
+    // TODO Switch between different layouts
+    bool update = false;
 
-        // Go to next line
-        penX = 0;
-        penY += fontMetrics.height;
+    switch( mLayout )
+    {
+      case LayoutEngine::SINGLE_LINE_BOX:
+      {
+        update = SingleLineLayout( layoutParameters,
+                                   glyphPositions,
+                                   actualSize );
+        break;
+      }
+      case LayoutEngine::MULTI_LINE_BOX:
+      {
+        update = MultiLineLayout( layoutParameters,
+                                  glyphPositions,
+                                  actualSize );
+        break;
+      }
+      default:
+        break;
+    }
+
+    return update;
+  }
+
+  // TODO - Rewrite this to handle bidi
+  bool SingleLineLayout( const LayoutParameters& layoutParameters,
+                         Vector<Vector2>& glyphPositions,
+                         Size& actualSize )
+  {
+    LineLayout layout;
+    layout.glyphIndex = 0u;
+    GetLineLayoutForBox( layoutParameters,
+                         layout );
 
-        actualSize.height += fontMetrics.height;
+    if( 0u == layout.numberOfGlyphs )
+    {
+      // The width is too small and no characters are laid-out.
+      return false;
+    }
+
+    // Update the actual size.
+    actualSize.width = layout.length;
+    actualSize.height = layout.height;
+
+    float penX = 0.f;
+    float penY = layout.height;
+
+    Vector2* glyphPositionsBuffer = glyphPositions.Begin();
+    for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
+    {
+      const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
+      Vector2& position = *( glyphPositionsBuffer + glyphIndex );
+
+      position.x = penX + glyph.xBearing;
+      position.y = penY - glyph.yBearing;
+
+      penX += glyph.advance;
+    }
+
+    return true;
+  }
+
+  // TODO - Rewrite this to handle bidi
+  bool MultiLineLayout( const LayoutParameters& layoutParameters,
+                        Vector<Vector2>& glyphPositions,
+                        Size& actualSize )
+  {
+    float penY = 0.f;
+    for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
+    {
+      float penX = 0.f;
+
+      // Get the layout for the line.
+      LineLayout layout;
+      layout.glyphIndex = index;
+      GetMultiLineLayoutForBox( layoutParameters,
+                                layout );
+
+      if( 0u == layout.numberOfGlyphs )
+      {
+        // The width is too small and no characters are laid-out.
+        return false;
       }
 
-      visualModel.SetGlyphPositions( &glyphPositions[0], glyphCount );
+      // Update the actual size.
+      if( layout.length > actualSize.width )
+      {
+        actualSize.width = layout.length;
+      }
+
+      actualSize.height += layout.height;
+
+      // Traverse the glyphs and set the positions.
+
+      penY += layout.height;
+
+      Vector2* glyphPositionsBuffer = glyphPositions.Begin();
+      for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
+      {
+        const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
+        Vector2& position = *( glyphPositionsBuffer + i );
+
+        position.x = penX + glyph.xBearing;
+        position.y = penY - glyph.yBearing;
+
+        penX += glyph.advance;
+      }
 
-      visualModel.SetActualSize( actualSize );
+      // Increase the glyph index.
+      index += layout.numberOfGlyphs;
     }
+
+    return true;
   }
 
-  unsigned int mLayout;
+  LayoutEngine::Layout mLayout;
 
   TextAbstraction::FontClient mFontClient;
 };
@@ -231,17 +422,13 @@ unsigned int LayoutEngine::GetLayout() const
   return mImpl->mLayout;
 }
 
-void LayoutEngine::UpdateVisualModel( const Vector2& boundingBox,
-                                      const Vector<GlyphInfo>& glyphs,
-                                      const Vector<CharacterIndex>& characterIndices,
-                                      const Vector<Length>& charactersPerGlyph,
-                                      VisualModel& visualModel )
+bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
+                               Vector<Vector2>& glyphPositions,
+                               Size& actualSize )
 {
-  mImpl->UpdateVisualModel( boundingBox,
-                            glyphs,
-                            characterIndices,
-                            charactersPerGlyph,
-                            visualModel );
+  return mImpl->LayoutText( layoutParameters,
+                            glyphPositions,
+                            actualSize );
 }
 
 } // namespace Text