X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Flayouts%2Flayout-engine.cpp;h=f4e11bfcae679c0abcde8912314fd8d23b17d971;hb=55e1976335d0098eff26d44a245de161a6a2a231;hp=f97a8ea96e2dac3a2ae81454637eeee4a2058f9c;hpb=74f7af1b08ce65dde5959d22b2d533cbc64b9d2e;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git diff --git a/dali-toolkit/internal/text/layouts/layout-engine.cpp b/dali-toolkit/internal/text/layouts/layout-engine.cpp index f97a8ea..f4e11bf 100755 --- a/dali-toolkit/internal/text/layouts/layout-engine.cpp +++ b/dali-toolkit/internal/text/layouts/layout-engine.cpp @@ -20,6 +20,7 @@ // EXTERNAL INCLUDES #include +#include #include #include @@ -49,10 +50,15 @@ namespace #endif const float MAX_FLOAT = std::numeric_limits::max(); -const bool RTL = true; -const float CURSOR_WIDTH = 1.f; +const CharacterDirection LTR = false; +const CharacterDirection RTL = !LTR; const float LINE_SPACING= 0.f; +inline bool isEmptyLineAtLast( const Vector& lines, const Vector::Iterator& line ) +{ + return ( (*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End() ); +} + } //namespace /** @@ -65,13 +71,13 @@ struct LineLayout characterIndex( 0u ), numberOfGlyphs( 0u ), numberOfCharacters( 0u ), - length( 0.f ), - extraBearing( 0.f ), - extraWidth( 0.f ), - wsLengthEndOfLine( 0.f ), - ascender( 0.f ), + ascender( -MAX_FLOAT ), descender( MAX_FLOAT ), - lineSpacing( 0.f ) + lineSpacing( 0.f ), + penX( 0.f ), + previousAdvance( 0.f ), + length( 0.f ), + wsLengthEndOfLine( 0.f ) {} ~LineLayout() @@ -83,10 +89,6 @@ struct LineLayout characterIndex = 0u; numberOfGlyphs = 0u; numberOfCharacters = 0u; - length = 0.f; - extraBearing = 0.f; - extraWidth = 0.f; - wsLengthEndOfLine = 0.f; ascender = 0.f; descender = MAX_FLOAT; } @@ -95,48 +97,52 @@ struct LineLayout CharacterIndex characterIndex; ///< Index of the first character to be laid-out. Length numberOfGlyphs; ///< The number of glyph which fit in one line. Length numberOfCharacters; ///< The number of characters which fit in one line. - float length; ///< The addition of the advance metric of all the glyphs which fit in one line. - float extraBearing; ///< The extra width to be added to the line's length when the bearing of the first glyph is negative. - float extraWidth; ///< The extra width to be added to the line's length when the bearing + width of the last glyph is greater than the advance. - float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line. float ascender; ///< The maximum ascender of all fonts in the line. float descender; ///< The minimum descender of all fonts in the line. float lineSpacing; ///< The line spacing + float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ). + float previousAdvance; ///< The advance of the previous glyph. + float length; ///< The current length of the line. + float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line. }; struct Engine::Impl { Impl() : mLayout( Layout::Engine::SINGLE_LINE_BOX ), - mCursorWidth( CURSOR_WIDTH ), - mDefaultLineSpacing( LINE_SPACING ), - mPreviousCharacterExtraWidth( 0.0f ) + mCursorWidth( 0.f ), + mDefaultLineSpacing( LINE_SPACING ) { } /** * @brief Updates the line ascender and descender with the metrics of a new font. * - * @param[in] fontId The id of the new font. + * @param[in] glyphMetrics The metrics of the new font. * @param[in,out] lineLayout The line layout. */ - void UpdateLineHeight( FontId fontId, LineLayout& lineLayout ) + void UpdateLineHeight( const GlyphMetrics& glyphMetrics, LineLayout& lineLayout ) { Text::FontMetrics fontMetrics; - mMetrics->GetFontMetrics( fontId, fontMetrics ); - - // Sets the maximum ascender. - if( fontMetrics.ascender > lineLayout.ascender ) + if( 0u != glyphMetrics.fontId ) { - lineLayout.ascender = fontMetrics.ascender; + mMetrics->GetFontMetrics( glyphMetrics.fontId, fontMetrics ); } - - // Sets the minimum descender. - if( fontMetrics.descender < lineLayout.descender ) + else { - lineLayout.descender = fontMetrics.descender; + fontMetrics.ascender = glyphMetrics.fontHeight; + fontMetrics.descender = 0.f; + fontMetrics.height = fontMetrics.ascender; + fontMetrics.underlinePosition = 0.f; + fontMetrics.underlineThickness = 1.f; } + // Sets the maximum ascender. + lineLayout.ascender = std::max( lineLayout.ascender, fontMetrics.ascender ); + + // Sets the minimum descender. + lineLayout.descender = std::min( lineLayout.descender, fontMetrics.descender ); + // set the line spacing lineLayout.lineSpacing = mDefaultLineSpacing; } @@ -152,28 +158,18 @@ struct Engine::Impl { lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters; lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs; - lineLayout.length += tmpLineLayout.length; - if( 0.f < tmpLineLayout.length ) - { - lineLayout.length += lineLayout.wsLengthEndOfLine; + lineLayout.penX = tmpLineLayout.penX; + lineLayout.previousAdvance = tmpLineLayout.previousAdvance; - lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine; - } - else - { - lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine; - } + lineLayout.length = tmpLineLayout.length; + lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine; - if( tmpLineLayout.ascender > lineLayout.ascender ) - { - lineLayout.ascender = tmpLineLayout.ascender; - } + // Sets the maximum ascender. + lineLayout.ascender = std::max( lineLayout.ascender, tmpLineLayout.ascender ); - if( tmpLineLayout.descender < lineLayout.descender ) - { - lineLayout.descender = tmpLineLayout.descender; - } + // Sets the minimum descender. + lineLayout.descender = std::min( lineLayout.descender, tmpLineLayout.descender ); } /** @@ -200,11 +196,21 @@ struct Engine::Impl { DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" ); DALI_LOG_INFO( gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex ); - // Stores temporary line layout which has not been added to the final line layout. - LineLayout tmpLineLayout; + + const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin(); + const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin(); + const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin(); + const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin(); + const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin(); + + const bool hasBidiParagraphs = !parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty(); + const CharacterDirection* const characterDirectionBuffer = hasBidiParagraphs ? parameters.textModel->mLogicalModel->mCharacterDirections.Begin() : nullptr; + + const float outlineWidth = static_cast( parameters.textModel->GetOutlineWidth() ); + const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count(); const bool isMultiline = mLayout == MULTI_LINE_BOX; - const bool isWordLaidOut = parameters.lineWrapMode == Text::LineWrap::WORD; + const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD; // The last glyph to be laid-out. const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs; @@ -216,30 +222,34 @@ struct Engine::Impl // Check whether the first glyph comes from a character that is shaped in multiple glyphs. const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( lineLayout.glyphIndex, lastGlyphOfParagraphPlusOne, - parameters.charactersPerGlyphBuffer ); + charactersPerGlyphBuffer ); GlyphMetrics glyphMetrics; GetGlyphsMetrics( lineLayout.glyphIndex, numberOfGLyphsInGroup, glyphMetrics, - parameters.glyphsBuffer, + glyphsBuffer, mMetrics ); // Set the direction of the first character of the line. - lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex ); - const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex ); - CharacterDirection previousCharacterDirection = firstCharacterDirection; + lineLayout.characterIndex = *( glyphsToCharactersBuffer + lineLayout.glyphIndex ); - const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance; - float tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f; + // Stores temporary line layout which has not been added to the final line layout. + LineLayout tmpLineLayout; - float tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f; + // Initialize the start point. - tmpLineLayout.length += mCursorWidth; // Added to give some space to the cursor. + // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph. + // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area. + // It needs to add as well space for the cursor if the text is in edit mode and extra space in case the text is outlined. + tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth; + + // Initialize the advance of the previous glyph. + tmpLineLayout.previousAdvance = 0.f; // Calculate the line height if there is no characters. FontId lastFontId = glyphMetrics.fontId; - UpdateLineHeight( lastFontId, tmpLineLayout ); + UpdateLineHeight( glyphMetrics, tmpLineLayout ); bool oneWordLaidOut = false; @@ -251,35 +261,35 @@ struct Engine::Impl // Check whether this glyph comes from a character that is shaped in multiple glyphs. const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex, lastGlyphOfParagraphPlusOne, - parameters.charactersPerGlyphBuffer ); + charactersPerGlyphBuffer ); GlyphMetrics glyphMetrics; GetGlyphsMetrics( glyphIndex, numberOfGLyphsInGroup, glyphMetrics, - parameters.glyphsBuffer, + glyphsBuffer, mMetrics ); - const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == parameters.totalNumberOfGlyphs; + const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs; // Check if the font of the current glyph is the same of the previous one. // If it's different the ascender and descender need to be updated. if( lastFontId != glyphMetrics.fontId ) { - UpdateLineHeight( glyphMetrics.fontId, tmpLineLayout ); + UpdateLineHeight( glyphMetrics, tmpLineLayout ); lastFontId = glyphMetrics.fontId; } // 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 + numberOfGLyphsInGroup - 1u ); + const Length charactersPerGlyph = *( charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u ); const bool hasCharacters = charactersPerGlyph > 0u; - const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex ); + const CharacterIndex characterFirstIndex = *( glyphsToCharactersBuffer + glyphIndex ); const CharacterIndex characterLastIndex = characterFirstIndex + ( hasCharacters ? charactersPerGlyph - 1u : 0u ); // Get the line break info for the current character. - const LineBreakInfo lineBreakInfo = hasCharacters ? *( parameters.lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK; + const LineBreakInfo lineBreakInfo = hasCharacters ? *( lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK; // Increase the number of characters. tmpLineLayout.numberOfCharacters += charactersPerGlyph; @@ -288,18 +298,17 @@ struct Engine::Impl tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup; // Check whether is a white space. - const Character character = *( parameters.textBuffer + characterFirstIndex ); + const Character character = *( textBuffer + characterFirstIndex ); const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character ); - // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character. - const float previousTmpLineLength = tmpLineLayout.length; - const float previousTmpExtraBearing = tmpExtraBearing; - const float previousTmpExtraWidth = tmpExtraWidth; + // Calculate the length of the line. - // Get the character's direction. - const CharacterDirection characterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + characterFirstIndex ); + // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character. + const float previousTmpPenX = tmpLineLayout.penX; + const float previousTmpAdvance = tmpLineLayout.previousAdvance; + const float previousTmpLength = tmpLineLayout.length; + const float previousTmpWsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine; - // Increase the accumulated length. if( isWhiteSpace ) { // Add the length to the length of white spaces at the end of the line. @@ -307,107 +316,17 @@ struct Engine::Impl } else { - // Add as well any previous white space length. - tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphMetrics.advance; - - // An extra space may be added to the line for the first and last glyph of the line. - // If the bearing of the first glyph is negative, its positive value needs to be added. - // If the bearing plus the width of the last glyph is greater than the advance, the difference - // needs to be added. - - if( characterDirection == paragraphDirection ) - { - if( RTL == characterDirection ) - { - // <-- - // | Rrrrr| - // or - // | Rllrrr| - // or - // |lllrrrrr| - // | Rll| - // - - tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f; - } - else // LTR - { - // --> - // |lllL | - // or - // |llrrL | - // or - // |lllllrrr| - // |rrL | - // - - const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance; - tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f; - tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth ); - } - } - else - { - if( characterDirection != previousCharacterDirection ) - { - if( RTL == characterDirection ) - { - // --> - // |lllR | - - const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance; - tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f; - tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth ); - } - else // LTR - { - // <-- - // | Lrrrr| - - tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f; - } - } - else if( characterDirection == firstCharacterDirection ) - { - if( RTL == characterDirection ) - { - // --> - // |llllllrr| - // |Rr | - - tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f; - } - else // LTR - { - // <-- - // |llllrrrr| - // | llL| - - const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance; - tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f; - tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth ); - } - } - } + tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.wsLengthEndOfLine; + tmpLineLayout.previousAdvance = ( glyphMetrics.advance + parameters.interGlyphExtraAdvance ); + tmpLineLayout.length = tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width; // Clear the white space length at the end of the line. tmpLineLayout.wsLengthEndOfLine = 0.f; } - // If calculation is end but wsLengthEndOfLine is exist, it means end of text is space. - // Merge remained length. - if ( !parameters.ignoreSpaceAfterText && glyphIndex == lastGlyphOfParagraphPlusOne-1 && tmpLineLayout.wsLengthEndOfLine > 0 ) - { - tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine; - tmpLineLayout.wsLengthEndOfLine = 0u; - } - - // Save the current extra width to compare with the next one - mPreviousCharacterExtraWidth = tmpExtraWidth; - // Check if the accumulated length fits in the width of the box. - if( ( completelyFill || isMultiline ) && !(parameters.ignoreSpaceAfterText && isWhiteSpace) && - ( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) ) + if( ( completelyFill || isMultiline ) && !isWhiteSpace && + ( tmpLineLayout.length > parameters.boundingBox.width ) ) { // Current word does not fit in the box's width. if( !oneWordLaidOut || completelyFill ) @@ -419,9 +338,11 @@ struct Engine::Impl { tmpLineLayout.numberOfCharacters -= charactersPerGlyph; tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup; - tmpLineLayout.length = previousTmpLineLength; - tmpExtraBearing = previousTmpExtraBearing; - tmpExtraWidth = previousTmpExtraWidth; + + tmpLineLayout.penX = previousTmpPenX; + tmpLineLayout.previousAdvance = previousTmpAdvance; + tmpLineLayout.length = previousTmpLength; + tmpLineLayout.wsLengthEndOfLine = previousTmpWsLengthEndOfLine; } // Add part of the word to the line layout. @@ -432,9 +353,6 @@ struct Engine::Impl DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Current word does not fit.\n" ); } - lineLayout.extraBearing = tmpExtraBearing; - lineLayout.extraWidth = tmpExtraWidth; - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" ); return; @@ -448,14 +366,11 @@ struct Engine::Impl // Set the next paragraph's direction. if( !isLastGlyph && - ( NULL != parameters.characterDirectionBuffer ) ) + ( nullptr != characterDirectionBuffer ) ) { - paragraphDirection = *( parameters.characterDirectionBuffer + 1u + characterLastIndex ); + paragraphDirection = *( characterDirectionBuffer + 1u + characterLastIndex ); } - lineLayout.extraBearing = tmpExtraBearing; - lineLayout.extraWidth = tmpExtraWidth; - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Must break\n" ); DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" ); @@ -475,19 +390,16 @@ struct Engine::Impl tmpLineLayout.Clear(); } - previousCharacterDirection = characterDirection; glyphIndex += numberOfGLyphsInGroup; } - lineLayout.extraBearing = tmpExtraBearing; - lineLayout.extraWidth = tmpExtraWidth; - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" ); } void SetGlyphPositions( const GlyphInfo* const glyphsBuffer, Length numberOfGlyphs, float outlineWidth, + float interGlyphExtraAdvance, Vector2* glyphPositionsBuffer ) { // Traverse the glyphs and set the positions. @@ -497,18 +409,17 @@ struct Engine::Impl // so the penX position needs to be moved to the right. const GlyphInfo& glyph = *glyphsBuffer; - float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing + outlineWidth : outlineWidth; - + float penX = -glyph.xBearing + mCursorWidth + outlineWidth; for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i ) { const GlyphInfo& glyph = *( glyphsBuffer + i ); Vector2& position = *( glyphPositionsBuffer + i ); - position.x = penX + glyph.xBearing; + position.x = std::roundf( penX + glyph.xBearing ); position.y = -glyph.yBearing; - penX += glyph.advance; + penX += ( glyph.advance + interGlyphExtraAdvance ); } } @@ -527,7 +438,7 @@ struct Engine::Impl Length& linesCapacity, bool updateCurrentBuffer ) { - LineRun* linesBuffer = NULL; + LineRun* linesBuffer = nullptr; // Reserve more space for the next lines. linesCapacity *= 2u; if( updateCurrentBuffer ) @@ -572,7 +483,7 @@ struct Engine::Impl const bool ellipsis = isAutoScrollEnabled ? ( penY - layout.descender > layoutParameters.boundingBox.height ) : ( ( penY - layout.descender > layoutParameters.boundingBox.height ) || ( ( mLayout == SINGLE_LINE_BOX ) && - ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) ); + ( layout.length > layoutParameters.boundingBox.width ) ) ); if( ellipsis ) { @@ -582,7 +493,7 @@ struct Engine::Impl // The last line needs to be completely filled with characters. // Part of a word may be used. - LineRun* lineRun = NULL; + LineRun* lineRun = nullptr; LineLayout ellipsisLayout; if( 0u != numberOfLines ) { @@ -613,10 +524,10 @@ struct Engine::Impl lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex; lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters; lineRun->width = ellipsisLayout.length; - lineRun->extraLength = ( ellipsisLayout.wsLengthEndOfLine > 0.f ) ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.extraWidth : 0.f; + lineRun->extraLength = std::ceil( ellipsisLayout.wsLengthEndOfLine ); lineRun->ascender = ellipsisLayout.ascender; lineRun->descender = ellipsisLayout.descender; - lineRun->direction = !RTL; + lineRun->direction = LTR; lineRun->ellipsis = true; layoutSize.width = layoutParameters.boundingBox.width; @@ -625,9 +536,13 @@ struct Engine::Impl layoutSize.height += ( lineRun->ascender + -lineRun->descender ) + lineRun->lineSpacing; } - SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun->glyphRun.glyphIndex, + const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin(); + const float outlineWidth = static_cast( layoutParameters.textModel->GetOutlineWidth() ); + + SetGlyphPositions( glyphsBuffer + lineRun->glyphRun.glyphIndex, ellipsisLayout.numberOfGlyphs, - layoutParameters.outlineWidth, + outlineWidth, + layoutParameters.interGlyphExtraAdvance, glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex ); } @@ -664,7 +579,7 @@ struct Engine::Impl if( isLastLine && !layoutParameters.isLastNewParagraph ) { - const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine; + const float width = layout.length + layout.wsLengthEndOfLine; if( MULTI_LINE_BOX == mLayout ) { lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width; @@ -678,12 +593,16 @@ struct Engine::Impl } else { - lineRun.width = layout.extraBearing + layout.length + layout.extraWidth; - lineRun.extraLength = ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f; + lineRun.width = layout.length; + lineRun.extraLength = std::ceil( layout.wsLengthEndOfLine ); } + + // Rounds upward to avoid a non integer size. + lineRun.width = std::ceil( lineRun.width ); + lineRun.ascender = layout.ascender; lineRun.descender = layout.descender; - lineRun.direction = !RTL; + lineRun.direction = LTR; lineRun.ellipsis = false; // Update the actual size. @@ -712,11 +631,16 @@ struct Engine::Impl LineRun* linesBuffer, Length& numberOfLines ) { + const Vector& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs; + // Need to add a new line with no characters but with height to increase the layoutSize.height - const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u ); + const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u]; Text::FontMetrics fontMetrics; - mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics ); + if( 0u != glyphInfo.fontId ) + { + mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics ); + } LineRun& lineRun = *( linesBuffer + numberOfLines ); ++numberOfLines; @@ -730,7 +654,7 @@ struct Engine::Impl lineRun.descender = fontMetrics.descender; lineRun.extraLength = 0.f; lineRun.alignmentOffset = 0.f; - lineRun.direction = !RTL; + lineRun.direction = LTR; lineRun.ellipsis = false; lineRun.lineSpacing = mDefaultLineSpacing; @@ -832,11 +756,15 @@ struct Engine::Impl UpdateLayoutSize( lines, layoutSize ); + // Rounds upward to avoid a non integer size. + layoutSize.height = std::ceil( layoutSize.height ); + // Nothing else do if there are no glyphs to layout. return false; } const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs; + const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count(); // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character. // This extra line needs to be removed. @@ -845,22 +773,27 @@ struct Engine::Impl Vector::Iterator lastLine = lines.End() - 1u; if( ( 0u == lastLine->characterRun.numberOfCharacters ) && - ( lastGlyphPlusOne == layoutParameters.totalNumberOfGlyphs ) ) + ( lastGlyphPlusOne == totalNumberOfGlyphs ) ) { lines.Remove( lastLine ); } } + // Retrieve BiDi info. + const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty(); + + const CharacterDirection* const characterDirectionBuffer = hasBidiParagraphs ? layoutParameters.textModel->mLogicalModel->mCharacterDirections.Begin() : nullptr; + // Set the first paragraph's direction. - CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL; + CharacterDirection paragraphDirection = ( nullptr != characterDirectionBuffer ) ? *characterDirectionBuffer : LTR; // Whether the layout is being updated or set from scratch. - const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < layoutParameters.totalNumberOfGlyphs; + const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs; - Vector2* glyphPositionsBuffer = NULL; + Vector2* glyphPositionsBuffer = nullptr; Vector newGlyphPositions; - LineRun* linesBuffer = NULL; + LineRun* linesBuffer = nullptr; Vector newLines; // Estimate the number of lines. @@ -883,9 +816,14 @@ struct Engine::Impl linesBuffer = lines.Begin(); } + + const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin(); + const float outlineWidth = static_cast( layoutParameters.textModel->GetOutlineWidth() ); + float penY = CalculateLineOffset( lines, layoutParameters.startLineIndex ); + for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; ) { CharacterDirection currentParagraphDirection = paragraphDirection; @@ -910,6 +848,10 @@ struct Engine::Impl DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" ); lines.Resize( numberOfLines ); + + // Rounds upward to avoid a non integer size. + layoutSize.height = std::ceil( layoutSize.height ); + return false; } @@ -942,7 +884,7 @@ struct Engine::Impl else { // Whether the last line has been laid-out. - const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs; + const bool isLastLine = index + layout.numberOfGlyphs == totalNumberOfGlyphs; if( numberOfLines == linesCapacity ) { @@ -964,7 +906,7 @@ struct Engine::Impl const GlyphIndex nextIndex = index + layout.numberOfGlyphs; - if( ( nextIndex == layoutParameters.totalNumberOfGlyphs ) && + if( ( nextIndex == totalNumberOfGlyphs ) && layoutParameters.isLastNewParagraph && ( mLayout == MULTI_LINE_BOX ) ) { @@ -990,9 +932,10 @@ struct Engine::Impl } // whether to add a last line. // Sets the positions of the glyphs. - SetGlyphPositions( layoutParameters.glyphsBuffer + index, + SetGlyphPositions( glyphsBuffer + index, layout.numberOfGlyphs, - layoutParameters.outlineWidth, + outlineWidth, + layoutParameters.interGlyphExtraAdvance, glyphPositionsBuffer + index - layoutParameters.startGlyphIndex ); // Updates the vertical pen's position. @@ -1008,7 +951,7 @@ struct Engine::Impl glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex, newGlyphPositions.Begin(), newGlyphPositions.End() ); - glyphPositions.Resize( layoutParameters.totalNumberOfGlyphs ); + glyphPositions.Resize( totalNumberOfGlyphs ); newLines.Resize( numberOfLines ); @@ -1041,6 +984,9 @@ struct Engine::Impl lines.Resize( numberOfLines ); } + // Rounds upward to avoid a non integer size. + layoutSize.height = std::ceil( layoutSize.height ); + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" ); return true; @@ -1051,6 +997,11 @@ struct Engine::Impl Length numberOfCharacters, Vector& glyphPositions ) { + const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin(); + const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin(); + const Length* const glyphsPerCharacterBuffer = layoutParameters.textModel->mVisualModel->mGlyphsPerCharacter.Begin(); + const float outlineWidth = static_cast( layoutParameters.textModel->GetOutlineWidth() ); + const CharacterIndex lastCharacterIndex = startIndex + numberOfCharacters; // Traverses the paragraphs with right to left characters. @@ -1071,9 +1022,9 @@ struct Engine::Impl } const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap; - const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) ); + const GlyphInfo& glyph = *( glyphsBuffer + *( charactersToGlyphsBuffer + characterVisualIndex ) ); - float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing - layoutParameters.outlineWidth : -layoutParameters.outlineWidth; + float penX = -glyph.xBearing + outlineWidth + mCursorWidth; Vector2* glyphPositionsBuffer = glyphPositions.Begin(); @@ -1086,20 +1037,20 @@ struct Engine::Impl const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex ); // Get the number of glyphs of the character. - const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex ); + const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterVisualIndex ); for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index ) { // Convert the character in the visual order into the glyph in the visual order. - const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index; + const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex ) + index; - DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs ); + DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count() ); - const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex ); + const GlyphInfo& glyph = *( glyphsBuffer + glyphIndex ); Vector2& position = *( glyphPositionsBuffer + glyphIndex ); - position.x = penX + glyph.xBearing; - penX += glyph.advance; + position.x = std::round( penX + glyph.xBearing ); + penX += ( glyph.advance + layoutParameters.interGlyphExtraAdvance ); } } } @@ -1130,12 +1081,18 @@ struct Engine::Impl continue; } - if( line.characterRun.characterIndex >= lastCharacterPlusOne ) + if( line.characterRun.characterIndex > lastCharacterPlusOne ) { // Do not align lines beyond the last laid-out character. break; } + if( line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast( lines, it ) ) + { + // Do not align lines beyond the last laid-out character unless the line is last and empty. + break; + } + // Calculate the line's alignment offset accordingly with the align option, // the box width, line length, and the paragraph's direction. CalculateHorizontalAlignment( size.width, @@ -1203,7 +1160,7 @@ struct Engine::Impl line.alignmentOffset -= line.extraLength; } - line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment. + line.alignmentOffset = std::floor( line.alignmentOffset ); // floor() avoids pixel alignment issues. break; } case HorizontalAlignment::END: @@ -1243,7 +1200,7 @@ struct Engine::Impl line.descender = 0.f; line.extraLength = 0.f; line.alignmentOffset = 0.f; - line.direction = !RTL; + line.direction = LTR; line.ellipsis = false; line.lineSpacing = mDefaultLineSpacing; } @@ -1251,13 +1208,12 @@ struct Engine::Impl Type mLayout; float mCursorWidth; float mDefaultLineSpacing; - float mPreviousCharacterExtraWidth; IntrusivePtr mMetrics; }; Engine::Engine() -: mImpl( NULL ) +: mImpl( nullptr ) { mImpl = new Engine::Impl(); }