From: Victor Cebollada Date: Wed, 18 Feb 2015 17:24:12 +0000 (+0000) Subject: Bidirectional Implementation for one line of text. X-Git-Tag: dali_1.0.38~11^2~63 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=2c7cfdb64f1a8281c8cbbe9531ec0b2ed779f915 Bidirectional Implementation for one line of text. Change-Id: I1480364c36e1ff4531dea494e4a8960c75a56242 Signed-off-by: Victor Cebollada --- diff --git a/dali-toolkit/internal/text/bidirectional-line-info-run.h b/dali-toolkit/internal/text/bidirectional-line-info-run.h index 90c8514..9aeea2a 100644 --- a/dali-toolkit/internal/text/bidirectional-line-info-run.h +++ b/dali-toolkit/internal/text/bidirectional-line-info-run.h @@ -37,7 +37,6 @@ struct BidirectionalLineInfoRun { CharacterRun characterRun; ///< The initial character index within the whole text and the number of characters of the run. CharacterIndex* visualToLogicalMap; ///< Pointer to the visual to logical map table. - CharacterIndex* logicalToVisualMap; ///< Pointer to the logical to visual map table. }; } // namespace Text diff --git a/dali-toolkit/internal/text/bidirectional-support.cpp b/dali-toolkit/internal/text/bidirectional-support.cpp index 5cf1ffb..14fb1a7 100644 --- a/dali-toolkit/internal/text/bidirectional-support.cpp +++ b/dali-toolkit/internal/text/bidirectional-support.cpp @@ -18,6 +18,9 @@ // FILE HEADER #include +// EXTERNAL INCLUDES +#include + namespace Dali { @@ -27,10 +30,135 @@ namespace Toolkit namespace Text { +namespace +{ + +/** + * @brief Get the lines of a paragraph. + * + * @param[in] paragraphInfo The paragraph. + * @param[in] lines The lines. + * @param[in] lineIndex Index pointing the first line to be checked. + * @param[out] firstLine Index to the first line of the paragraph. + * @param[out] numberOfLines The number of lines. + */ +void GetLines( const BidirectionalParagraphInfoRun& paragraphInfo, + const Vector& lines, + unsigned int lineIndex, + unsigned int& firstLine, + unsigned int& numberOfLines ) +{ + firstLine = lineIndex; + numberOfLines = 0u; + + const CharacterIndex lastCharacterIndex = paragraphInfo.characterRun.characterIndex + paragraphInfo.characterRun.numberOfCharacters; + bool firstLineFound = false; + + for( Vector::ConstIterator it = lines.Begin() + lineIndex, + endIt = lines.End(); + it != endIt; + ++it ) + { + const LineRun& line = *it; + + if( ( line.characterRun.characterIndex + line.characterRun.numberOfCharacters > paragraphInfo.characterRun.characterIndex ) && + ( lastCharacterIndex > line.characterRun.characterIndex ) ) + { + firstLineFound = true; + ++numberOfLines; + } + else if( lastCharacterIndex <= line.characterRun.characterIndex ) + { + // nothing else to do. + break; + } + + if( !firstLineFound ) + { + ++firstLine; + } + } +} + +} // namespace + void SetBidirectionalInfo( const Vector& text, + const Vector& scripts, const Vector& lineBreakInfo, Vector& bidirectionalInfo ) { + // Traverse the script runs. If there is one with a right to left script, create the bidirectional info for the paragraph containing that script is needed. + // From the bidirectional point of view, a paragraph is the piece of text between two LINE_MUST_BREAK. + + // Index pointing the first character of the current paragraph. + CharacterIndex paragraphCharacterIndex = 0u; + + // Pointer to the text buffer. + const Character* textBuffer = text.Begin(); + + // Pointer to the line break info buffer. + const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin(); + + // The number of characters. + const Length numberOfCharacters = text.Count(); + + // Handle to the bidirectional info module in text-abstraction. + TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get(); + + for( Vector::ConstIterator it = scripts.Begin(), + endIt = scripts.End(); + it != endIt; + ++it ) + { + const ScriptRun& scriptRun = *it; + const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters; + + if( TextAbstraction::IsRightToLeftScript( scriptRun.script ) && // The script is right to left. + ( lastScriptRunIndex > paragraphCharacterIndex ) ) // It isn't part of a previous paragraph. + { + // Find the paragraphs which contains this script run. + // Consider: + // 1) Different paragraphs may contain this script run. + // ------||------------------- rtl sr ------------------------||------------------- + // --||----- p -----||------------------ p -------------||-------- p ------||------ + // + // 2) The paragraph which contains this script run may contain other right to left script runs. + // -----||--- rtl sr ---||---- ltr sr ----||---------- rtl sr -----------||-------- + // -----||---------------------------------- p -----------------------------------| + + while( lastScriptRunIndex > paragraphCharacterIndex ) + { + // There is a paragraph which contains the current script. + + Length index = paragraphCharacterIndex; + while( ( index < numberOfCharacters ) && ( paragraphCharacterIndex < lastScriptRunIndex ) ) + { + if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index ) ) + { + if( index >= scriptRun.characterRun.characterIndex ) + { + // The Bidirectional run must have the same number of characters than the paragraph. + BidirectionalParagraphInfoRun bidirectionalRun; + bidirectionalRun.characterRun.characterIndex = paragraphCharacterIndex; + bidirectionalRun.characterRun.numberOfCharacters = ( index - paragraphCharacterIndex ) + 1u; // The must break character is part of the paragrah. + + // Create the bidirectional info for the whole paragraph and store the index to the table with this info in the run. + bidirectionalRun.bidirectionalInfoIndex = bidirectionalSupport.CreateInfo( textBuffer + bidirectionalRun.characterRun.characterIndex, + bidirectionalRun.characterRun.numberOfCharacters ); + + bidirectionalInfo.PushBack( bidirectionalRun ); + } + + // Update the character index of the next paragraph. + paragraphCharacterIndex = index + 1u; + } + ++index; + } + + // The last character is always a must-break, so there is no need to check if there is characters left. + } + } + } } void ReplaceBidirectionalInfo( LogicalModel& model, @@ -41,9 +169,63 @@ void ReplaceBidirectionalInfo( LogicalModel& model, } void ReorderLines( const Vector& bidirectionalInfo, - const Vector& lineRuns, + const Vector& lineRuns, Vector& lineInfoRuns ) { + // Handle to the bidirectional info module in text-abstraction. + TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get(); + + // Keep an index to the first line to be checked if it's contained inside the paragraph. + // Avoids check the lines from the beginning for each paragraph. + unsigned int lineIndex = 0u; + + for( Vector::ConstIterator it = bidirectionalInfo.Begin(), + endIt = bidirectionalInfo.End(); + it != endIt; + ++it ) + { + const BidirectionalParagraphInfoRun& paragraphInfo = *it; + + // Get the lines for this paragraph. + unsigned int firstLine = 0u; + unsigned int numberOfLines = 0u; + + // Get an index to the first line and the number of lines of the current paragraph. + GetLines( paragraphInfo, + lineRuns, + lineIndex, + firstLine, + numberOfLines ); + + lineIndex = firstLine + numberOfLines; + + // Traverse the lines and reorder them + for( Vector::ConstIterator lineIt = lineRuns.Begin() + firstLine, + endLineIt = lineRuns.Begin() + firstLine + numberOfLines; + lineIt != endLineIt; + ++lineIt ) + { + const LineRun& line = *lineIt; + + // Creates a bidirectional info for the line run. + BidirectionalLineInfoRun lineInfoRun; + lineInfoRun.characterRun.characterIndex = line.characterRun.characterIndex; + lineInfoRun.characterRun.numberOfCharacters = line.characterRun.numberOfCharacters; + + // Allocate space for the conversion maps. + // The memory is freed after the visual to logical to visual conversion tables are built in the logical model. + lineInfoRun.visualToLogicalMap = reinterpret_cast( malloc( line.characterRun.numberOfCharacters * sizeof( CharacterIndex ) ) ); + + // Reorders the line. + bidirectionalSupport.Reorder( paragraphInfo.bidirectionalInfoIndex, + line.characterRun.characterIndex, + line.characterRun.numberOfCharacters, + lineInfoRun.visualToLogicalMap ); + + // Push the run into the vector. + lineInfoRuns.PushBack( lineInfoRun ); + } + } } void ReorderLines( LogicalModel& logicalModel, diff --git a/dali-toolkit/internal/text/bidirectional-support.h b/dali-toolkit/internal/text/bidirectional-support.h index a54bc19..4bdd5e3 100644 --- a/dali-toolkit/internal/text/bidirectional-support.h +++ b/dali-toolkit/internal/text/bidirectional-support.h @@ -18,10 +18,14 @@ * */ -// INTERNAL INCLUDES +// EXTERNAL INCLUDES #include + +// INTERNAL INCLUDES #include #include +#include +#include namespace Dali { @@ -39,10 +43,12 @@ class VisualModel; * Sets the bidirectional info into the logical model. * * @param[in] text Vector of UTF-32 characters. + * @param[in] scripts Vector containing the script runs for the whole text. * @param[in] lineBreakInfo The line break info. * @param[out] bidirectionalInfo Vector with the bidirectional infor for each paragraph. */ void SetBidirectionalInfo( const Vector& text, + const Vector& scripts, const Vector& lineBreakInfo, Vector& bidirectionalInfo ); @@ -79,7 +85,7 @@ void ReplaceBidirectionalInfo( LogicalModel& model, * @param[out] lineInfoRuns line runs with the visual to logical and logical to visual conversion maps. */ void ReorderLines( const Vector& bidirectionalInfo, - const Vector& lineRuns, + const Vector& lineRuns, Vector& lineInfoRuns ); /** diff --git a/dali-toolkit/internal/text/character-set-conversion.h b/dali-toolkit/internal/text/character-set-conversion.h index 1e49357..2fca983 100644 --- a/dali-toolkit/internal/text/character-set-conversion.h +++ b/dali-toolkit/internal/text/character-set-conversion.h @@ -18,12 +18,12 @@ * */ -// INTERNAL INCLUDES -#include - // EXTERNAL INCLUDES #include +// INTERNAL INCLUDES +#include + namespace Dali { diff --git a/dali-toolkit/internal/text/layouts/layout-engine.cpp b/dali-toolkit/internal/text/layouts/layout-engine.cpp index 9544a66..86ddbb4 100644 --- a/dali-toolkit/internal/text/layouts/layout-engine.cpp +++ b/dali-toolkit/internal/text/layouts/layout-engine.cpp @@ -24,6 +24,7 @@ // INTERNAL INCLUDES #include +#include namespace Dali { @@ -293,7 +294,43 @@ struct LayoutEngine::Impl return update; } - // TODO - Rewrite this to handle bidi + void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters, + Vector& glyphPositions ) + { + for( Length lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex ) + { + const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer +lineIndex ); + + float penX = 0.f; + + Vector2* glyphPositionsBuffer = glyphPositions.Begin(); + + for( CharacterIndex characterLogicalIndex = 0u; + characterLogicalIndex < bidiLine.characterRun.numberOfCharacters; + ++characterLogicalIndex ) + { + // Convert the character in the logical order into the character in the visual order. + const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex ); + + // Get the number of glyphs of the character. + const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex ); + + for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index ) + { + // Convert the character in the visual order into the glyph in the visual order. + GlyphIndex glyphIndex = 1u + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex + index ) - numberOfGlyphs; + + const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex ); + Vector2& position = *( glyphPositionsBuffer + glyphIndex ); + + position.x = penX + glyph.xBearing; + + penX += glyph.advance; + } + } + } + } + bool SingleLineLayout( const LayoutParameters& layoutParameters, Vector& glyphPositions, Vector& lines, @@ -339,7 +376,6 @@ struct LayoutEngine::Impl return true; } - // TODO - Rewrite this to handle bidi bool MultiLineLayout( const LayoutParameters& layoutParameters, Vector& glyphPositions, Vector& lines, @@ -443,6 +479,13 @@ bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters, actualSize ); } +void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters, + Vector& glyphPositions ) +{ + mImpl->ReLayoutRightToLeftLines( layoutParameters, + glyphPositions ); +} + } // namespace Text } // namespace Toolkit diff --git a/dali-toolkit/internal/text/layouts/layout-engine.h b/dali-toolkit/internal/text/layouts/layout-engine.h index 59977c2..cdcd156 100644 --- a/dali-toolkit/internal/text/layouts/layout-engine.h +++ b/dali-toolkit/internal/text/layouts/layout-engine.h @@ -18,13 +18,13 @@ * */ -// INTERNAL INCLUDE -#include - // EXTERNAL INCLUDE #include #include +// INTERNAL INCLUDE +#include + namespace Dali { @@ -90,6 +90,17 @@ public: Vector& lines, Size& actualSize ); + /** + * Re-lays out those lines with right to left characters. + * + * It doesn't change the phisical position of the glyphs in the model but sets their new position. + * + * @param[in] layoutParameters The parameters needed to layout the text. + * @param[in,out] glyphPositions The positions of all the glyphs. + */ + void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters, + Vector& glyphPositions ); + private: // Undefined diff --git a/dali-toolkit/internal/text/layouts/layout-parameters.h b/dali-toolkit/internal/text/layouts/layout-parameters.h index 9fa4dde..808e1ae 100644 --- a/dali-toolkit/internal/text/layouts/layout-parameters.h +++ b/dali-toolkit/internal/text/layouts/layout-parameters.h @@ -33,6 +33,8 @@ namespace Toolkit namespace Text { +struct BidirectionalLineInfoRun; + /** * @brief Struct used to pass parameters. */ @@ -62,7 +64,11 @@ struct LayoutParameters totalNumberOfGlyphs( totalNumberOfGlyphs ), glyphsBuffer( glyphsBuffer ), glyphsToCharactersBuffer( glyphsToCharactersBuffer ), - charactersPerGlyphBuffer( charactersPerGlyphBuffer ) + charactersPerGlyphBuffer( charactersPerGlyphBuffer ), + charactersToGlyphsBuffer( NULL ), + glyphsPerCharacterBuffer( NULL ), + lineBidirectionalInfoRunsBuffer( NULL ), + numberOfBidirectionalInfoRuns( 0u ) {} Vector2 boundingBox; @@ -72,6 +78,10 @@ struct LayoutParameters const GlyphInfo* const glyphsBuffer; const CharacterIndex* const glyphsToCharactersBuffer; const Length* const charactersPerGlyphBuffer; + GlyphIndex* charactersToGlyphsBuffer; ///< The character to glyph conversion table. + Length* glyphsPerCharacterBuffer; ///< The number of glyphs per character. + BidirectionalLineInfoRun* lineBidirectionalInfoRunsBuffer; ///< Bidirectional conversion tables per line. + Length numberOfBidirectionalInfoRuns; ///< The number of lines with bidirectional info. }; } // namespace Text diff --git a/dali-toolkit/internal/text/logical-model.cpp b/dali-toolkit/internal/text/logical-model.cpp index 16d96aa..b91b285 100644 --- a/dali-toolkit/internal/text/logical-model.cpp +++ b/dali-toolkit/internal/text/logical-model.cpp @@ -18,15 +18,15 @@ // CLASS HEADER #include +// EXTERNAL INCLUDES +#include + // INTERNAL INCLUDES #include #include #include #include -// EXTERNAL INCLUDES -#include - namespace Dali { @@ -50,14 +50,20 @@ struct GetRunCache struct LogicalModel::Impl { - Vector mText; - Vector mScriptRuns; - Vector mFontRuns; - Vector mLineBreakInfo; - Vector mWordBreakInfo; + Vector mText; + Vector mScriptRuns; + Vector mFontRuns; + Vector mLineBreakInfo; + Vector mWordBreakInfo; + Vector mBidirectionalParagraphInfo; + + Vector mBidirectionalLineInfo; + Vector mLogicalToVisualMap; ///< Bidirectional logical to visual conversion table. + Vector mVisualToLogicalMap; ///< Bidirectional visual to logical conversion table. - GetRunCache mGetScriptCache; ///< Caches the GetNumberOfScriptRuns( characterIndex, numberOfCharacters ) operation. - GetRunCache mGetFontCache; ///< Caches the GetNumberOfFontRuns( characterIndex, numberOfCharacters ) operation. + GetRunCache mGetScriptCache; ///< Caches the GetNumberOfScriptRuns( characterIndex, numberOfCharacters ) operation. + GetRunCache mGetFontCache; ///< Caches the GetNumberOfFontRuns( characterIndex, numberOfCharacters ) operation. + GetRunCache mGetBidirectionalCache; ///< Caches the GetNumberOfBidirectionalInfoRuns( characterIndex, numberOfCharacters ) operation. }; LogicalModelPtr LogicalModel::New() @@ -86,8 +92,8 @@ Length LogicalModel::GetNumberOfCharacters() const return mImpl->mText.Count(); } -void LogicalModel::GetText( CharacterIndex characterIndex, - Character* text, +void LogicalModel::GetText( Character* text, + CharacterIndex characterIndex, Length numberOfCharacters ) const { Vector& modelText = mImpl->mText; @@ -376,12 +382,87 @@ WordBreakInfo LogicalModel::GetWordBreakInfo( CharacterIndex characterIndex ) co void LogicalModel::SetBidirectionalInfo( const BidirectionalParagraphInfoRun* const bidirectionalInfo, Length numberOfRuns ) { + Vector& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo; + + if( 0u == numberOfRuns ) + { + modelBidirectionalParagraphInfo.Clear(); + } + else + { + modelBidirectionalParagraphInfo.Resize( numberOfRuns ); + memcpy( modelBidirectionalParagraphInfo.Begin(), bidirectionalInfo, numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) ); + } + + mImpl->mGetBidirectionalCache.characterIndex = 0u; + mImpl->mGetBidirectionalCache.numberOfCharacters = 0u; + mImpl->mGetBidirectionalCache.firstRun = 0u; + mImpl->mGetBidirectionalCache.numberOfRuns = 0u; } Length LogicalModel::GetNumberOfBidirectionalInfoRuns( CharacterIndex characterIndex, Length numberOfCharacters ) const { - return 0u; + GetRunCache& bidiCache = mImpl->mGetBidirectionalCache; + + // Set the character index and the number of characters into the cache. + bidiCache.characterIndex = characterIndex; + bidiCache.numberOfCharacters = numberOfCharacters; + + if( ( 0u == characterIndex ) && + ( mImpl->mText.Count() == numberOfCharacters ) ) + { + bidiCache.firstRun = 0u; + bidiCache.numberOfRuns = mImpl->mBidirectionalParagraphInfo.Count(); + return bidiCache.numberOfRuns; + } + + // Initialize the number of bidi paragraphs and the index to the first paragraph. + bidiCache.firstRun = 0u; + bidiCache.numberOfRuns = 0; + bool firstParagraphFound = false; + + const Vector& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo; + + // Traverse the bidirectional paragraph info and count those bidi paragraphs within the range of characters. + for( Vector::ConstIterator it = modelBidirectionalParagraphInfo.Begin(), + endIt = modelBidirectionalParagraphInfo.End(); + it != endIt; + ++it ) + { + const BidirectionalParagraphInfoRun& bidi = *it; + + if( ( bidi.characterRun.characterIndex + bidi.characterRun.numberOfCharacters > characterIndex ) && + ( characterIndex + numberOfCharacters > bidi.characterRun.characterIndex ) ) + { + firstParagraphFound = true; + ++bidiCache.numberOfRuns; + } + + if( !firstParagraphFound ) + { + ++bidiCache.firstRun; + } + } + + return bidiCache.numberOfRuns; +} + +void LogicalModel::GetBidirectionalInfo( BidirectionalParagraphInfoRun* bidirectionalInfo, + CharacterIndex characterIndex, + Length numberOfCharacters ) const +{ + const Vector& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo; + GetRunCache& bidiCache = mImpl->mGetBidirectionalCache; + + if( ( characterIndex != bidiCache.characterIndex ) || + ( numberOfCharacters != bidiCache.numberOfCharacters ) ) + { + GetNumberOfBidirectionalInfoRuns( characterIndex, + numberOfCharacters ); + } + + memcpy( bidirectionalInfo, modelBidirectionalParagraphInfo.Begin() + bidiCache.firstRun, bidiCache.numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) ); } void LogicalModel::GetCharacterDirections( CharacterDirection* directions, @@ -398,28 +479,96 @@ CharacterDirection LogicalModel::GetCharacterDirection( CharacterIndex character void LogicalModel::SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo, Length numberOfRuns ) { + Vector& modelVisualToLogicalMap = mImpl->mVisualToLogicalMap; + Vector& modelLogicalToVisualMap = mImpl->mLogicalToVisualMap; + + if( 0u == numberOfRuns ) + { + modelVisualToLogicalMap.Clear(); + modelLogicalToVisualMap.Clear(); + } + else + { + const Length numberOfCharacters = mImpl->mText.Count(); + modelVisualToLogicalMap.Resize( numberOfCharacters ); + modelLogicalToVisualMap.Resize( numberOfCharacters ); + + CharacterIndex* modelVisualToLogicalMapBuffer = modelVisualToLogicalMap.Begin(); + CharacterIndex* modelLogicalToVisualMapBuffer = modelLogicalToVisualMap.Begin(); + + CharacterIndex lastIndex = 0u; + for( unsigned int bidiIndex = 0u; bidiIndex < numberOfRuns; ++bidiIndex ) + { + const BidirectionalLineInfoRun& bidiLineInfo = *( bidirectionalInfo + bidiIndex ); + + if( lastIndex < bidiLineInfo.characterRun.characterIndex ) + { + // Fill with the identity. + for( ; lastIndex < bidiLineInfo.characterRun.characterIndex; ++lastIndex ) + { + *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex; + } + } + + // Fill the conversion table of the run. + for( CharacterIndex index = 0u; + index < bidiLineInfo.characterRun.numberOfCharacters; + ++index, ++lastIndex ) + { + *( modelVisualToLogicalMapBuffer + lastIndex ) = bidiLineInfo.characterRun.characterIndex + *( bidiLineInfo.visualToLogicalMap + index ); + } + } + + // Complete with the identity if there are some left to right characters after the last right to left. + for( ; lastIndex < numberOfCharacters; ++lastIndex ) + { + *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex; + } + + // Sets the logical to visual conversion map. + for( CharacterIndex index = 0u; index < numberOfCharacters; ++index ) + { + *( modelLogicalToVisualMapBuffer + *( modelVisualToLogicalMapBuffer + index ) ) = index; + } + } } CharacterIndex LogicalModel::GetVisualCharacterIndex( CharacterIndex logicalCharacterIndex ) const { - return 0u; + if( 0u == mImpl->mLogicalToVisualMap.Count() ) + { + // If there is no logical to visual info is because the whole text is left to right. + // Return the identity. + return logicalCharacterIndex; + } + + return *( mImpl->mLogicalToVisualMap.Begin() + logicalCharacterIndex ); } CharacterIndex LogicalModel::GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex ) const { - return 0u; + if( 0u == mImpl->mVisualToLogicalMap.Count() ) + { + // If there is no visual to logical info is because the whole text is left to right. + // Return the identity. + return visualCharacterIndex; + } + + return *( mImpl->mVisualToLogicalMap.Begin() + visualCharacterIndex ); } void LogicalModel::GetLogicalToVisualMap( CharacterIndex* logicalToVisualMap, CharacterIndex characterIndex, Length numberOfCharacters ) const { + memcpy( logicalToVisualMap, mImpl->mLogicalToVisualMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) ); } void LogicalModel::GetVisualToLogicalMap( CharacterIndex* visualToLogicalMap, CharacterIndex characterIndex, Length numberOfCharacters ) const { + memcpy( visualToLogicalMap, mImpl->mVisualToLogicalMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) ); } LogicalModel::~LogicalModel() diff --git a/dali-toolkit/internal/text/logical-model.h b/dali-toolkit/internal/text/logical-model.h index 5d14098..8e3f5df 100644 --- a/dali-toolkit/internal/text/logical-model.h +++ b/dali-toolkit/internal/text/logical-model.h @@ -18,10 +18,12 @@ * */ -// INTERNAL INCLUDES +// EXTERNAL INCLUDES #include #include #include + +// INTERNAL INCLUDES #include namespace Dali @@ -81,12 +83,12 @@ public: * @brief Retrieves characters from the text in the given buffer. * * @pre The size of the @p text buffer needs to be big enough to copy the @p numberOfCharacters. - * @param[in] characterIndex The index to the first character to copy. * @param[out] text Pointer to a buffer where the text is copied. + * @param[in] characterIndex The index to the first character to copy. * @param[in] numberOfCharacters The number of characters to be copied. */ - void GetText( CharacterIndex characterIndex, - Character* text, + void GetText( Character* text, + CharacterIndex characterIndex, Length numberOfCharacters ) const; /** @@ -332,6 +334,21 @@ public: Length numberOfCharacters ) const; /** + * Retrieves the bidirectional paragraph info runs for the given range of characters. + * + * The @p bidirectionalInfo buffer needs to be big enough to copy the number of bidirectional + * paragraph info runs. + * Call GetNumberOfBidirectionalInfoRuns() to retrieve the number of bidirectional runs. + * + * @param[out] bidirectionalInfo Pointer to a buffer where the bidirectional info runs are copied. + * @param[in] characterIndex Index to the first character. + * @param[in] numberOfCharacters The number of characters. + */ + void GetBidirectionalInfo( BidirectionalParagraphInfoRun* bidirectionalInfo, + CharacterIndex characterIndex, + Length numberOfCharacters ) const; + + /** * Retrieves the direction of the characters. * * It sets @c true for right to left characters and @c false for left to right. diff --git a/dali-toolkit/internal/text/multi-language-support-impl.h b/dali-toolkit/internal/text/multi-language-support-impl.h index bcf0a6b..0f7e177 100644 --- a/dali-toolkit/internal/text/multi-language-support-impl.h +++ b/dali-toolkit/internal/text/multi-language-support-impl.h @@ -18,12 +18,12 @@ * */ -// INTERNAL INCLUDES -#include - // EXTERNAL INCLUDES #include +// INTERNAL INCLUDES +#include + namespace Dali { diff --git a/dali-toolkit/internal/text/multi-language-support.h b/dali-toolkit/internal/text/multi-language-support.h index b0873d9..b70f672 100644 --- a/dali-toolkit/internal/text/multi-language-support.h +++ b/dali-toolkit/internal/text/multi-language-support.h @@ -18,9 +18,11 @@ * */ -// INTERNAL INCLUDES +// EXTERNAL INCLUDES #include #include + +// INTERNAL INCLUDES #include #include diff --git a/dali-toolkit/internal/text/segmentation.h b/dali-toolkit/internal/text/segmentation.h index 27fb85d..949b1c9 100644 --- a/dali-toolkit/internal/text/segmentation.h +++ b/dali-toolkit/internal/text/segmentation.h @@ -18,8 +18,10 @@ * */ -// INTERNAL INCLUDES +// EXTERNAL INCLUDES #include + +// INTERNAL INCLUDES #include namespace Dali diff --git a/dali-toolkit/internal/text/shaper.h b/dali-toolkit/internal/text/shaper.h index c1ccfb5..d423cc5 100644 --- a/dali-toolkit/internal/text/shaper.h +++ b/dali-toolkit/internal/text/shaper.h @@ -18,8 +18,10 @@ * */ -// INTERNAL INCLUDES +// EXTERNAL INCLUDES #include + +// INTERNAL INCLUDES #include #include diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp index c3bec4f..4395e1b 100644 --- a/dali-toolkit/internal/text/text-controller.cpp +++ b/dali-toolkit/internal/text/text-controller.cpp @@ -18,7 +18,14 @@ // CLASS HEADER #include +// EXTERNAL INCLUDES +#include +#include +#include +#include + // INTERNAL INCLUDES +#include #include #include #include @@ -30,12 +37,6 @@ #include #include -// EXTERNAL INCLUDES -#include -#include -#include -#include - using std::vector; namespace @@ -434,6 +435,18 @@ void Controller::SetText( const std::string& text ) // The natural size needs to be re-calculated. mImpl->mRecalculateNaturalSize = true; + // Reset buffers. + mImpl->mLogicalModel->SetText( NULL, 0u ); + mImpl->mLogicalModel->SetScripts( NULL, 0u ); + mImpl->mLogicalModel->SetFonts( NULL, 0u ); + mImpl->mLogicalModel->SetLineBreakInfo( NULL, 0u ); + mImpl->mLogicalModel->SetWordBreakInfo( NULL, 0u ); + mImpl->mLogicalModel->SetBidirectionalInfo( NULL, 0u ); + mImpl->mLogicalModel->SetVisualToLogicalMap( NULL, 0u ); + mImpl->mVisualModel->SetGlyphs( NULL, NULL, NULL, 0u ); + mImpl->mVisualModel->SetGlyphPositions( NULL, 0u ); + mImpl->mVisualModel->SetLines( NULL, 0u ); + if( mImpl->mTextInput ) { // Cancel previously queued events @@ -687,6 +700,68 @@ bool Controller::DoRelayout( const Vector2& size, } } + Vector bidirectionalInfo; + if( BIDI_INFO & operations ) + { + // Some vectors with data needed to get the paragraph's bidirectional info may be void + // after the first time the text has been laid out. + // Fill the vectors again. + + const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters(); + + if( 0u == utf32Characters.Count() ) + { + utf32Characters.Resize( numberOfCharacters ); + + mImpl->mLogicalModel->GetText( utf32Characters.Begin(), + 0u, + numberOfCharacters ); + } + + if( 0u == lineBreakInfo.Count() ) + { + lineBreakInfo.Resize( numberOfCharacters ); + + mImpl->mLogicalModel->GetLineBreakInfo( lineBreakInfo.Begin(), + 0u, + numberOfCharacters ); + } + + if( 0u == scripts.Count() ) + { + scripts.Resize( mImpl->mLogicalModel->GetNumberOfScriptRuns( 0u, + numberOfCharacters ) ); + mImpl->mLogicalModel->GetScriptRuns( scripts.Begin(), + 0u, + numberOfCharacters ); + } + + // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's + // bidirectional info. + + Length numberOfParagraphs = 0u; + + const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin(); + for( Length index = 0u; index < characterCount; ++index ) + { + if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) ) + { + ++numberOfParagraphs; + } + } + + bidirectionalInfo.Reserve( numberOfParagraphs ); + + // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts. + SetBidirectionalInfo( utf32Characters, + scripts, + lineBreakInfo, + bidirectionalInfo ); + + mImpl->mLogicalModel->SetBidirectionalInfo( bidirectionalInfo.Begin(), + bidirectionalInfo.Count() ); + } + Vector glyphs; Vector glyphsToCharactersMap; Vector charactersPerGlyph; @@ -719,34 +794,48 @@ bool Controller::DoRelayout( const Vector2& size, if( LAYOUT & operations ) { + // Some vectors with data needed to layout and reorder may be void + // after the first time the text has been laid out. + // Fill the vectors again. + const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters(); + numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs(); - if( 0u == numberOfGlyphs ) + if( 0u == lineBreakInfo.Count() ) { - numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs(); - lineBreakInfo.Resize( numberOfCharacters ); - wordBreakInfo.Resize( numberOfCharacters ); - glyphs.Resize( numberOfGlyphs ); - glyphsToCharactersMap.Resize( numberOfGlyphs ); - charactersPerGlyph.Resize( numberOfGlyphs ); - mImpl->mLogicalModel->GetLineBreakInfo( lineBreakInfo.Begin(), 0u, numberOfCharacters ); + } + if( 0u == wordBreakInfo.Count() ) + { + wordBreakInfo.Resize( numberOfCharacters ); mImpl->mLogicalModel->GetWordBreakInfo( wordBreakInfo.Begin(), 0u, numberOfCharacters ); + } + if( 0u == glyphs.Count() ) + { + glyphs.Resize( numberOfGlyphs ); mImpl->mVisualModel->GetGlyphs( glyphs.Begin(), 0u, numberOfGlyphs ); + } + if( 0u == glyphsToCharactersMap.Count() ) + { + glyphsToCharactersMap.Resize( numberOfGlyphs ); mImpl->mVisualModel->GetGlyphToCharacterMap( glyphsToCharactersMap.Begin(), 0u, numberOfGlyphs ); + } + if( 0u == charactersPerGlyph.Count() ) + { + charactersPerGlyph.Resize( numberOfGlyphs ); mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(), 0u, numberOfGlyphs ); @@ -780,6 +869,76 @@ bool Controller::DoRelayout( const Vector2& size, if( viewUpdated ) { + // Reorder the lines + if( REORDER & operations ) + { + const Length numberOfBidiParagraphs = mImpl->mLogicalModel->GetNumberOfBidirectionalInfoRuns( 0u, numberOfCharacters ); + + if( 0u == bidirectionalInfo.Count() ) + { + bidirectionalInfo.Resize( numberOfBidiParagraphs ); + mImpl->mLogicalModel->GetBidirectionalInfo( bidirectionalInfo.Begin(), + 0u, + numberOfCharacters ); + } + + // Check first if there are paragraphs with bidirectional info. + if( 0u != bidirectionalInfo.Count() ) + { + // Get the lines + const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines(); + + // Reorder the lines. + Vector lineBidirectionalInfoRuns; + lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters. + ReorderLines( bidirectionalInfo, + lines, + lineBidirectionalInfoRuns ); + + // Set the bidirectional info into the model. + const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count(); + mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(), + numberOfBidirectionalInfoRuns ); + + // Set the bidirectional info per line into the layout parameters. + layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin(); + layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns; + + // Get the character to glyph conversion table and set into the layout. + Vector characterToGlyphMap; + characterToGlyphMap.Resize( numberOfCharacters ); + + layoutParameters.charactersToGlyphsBuffer = characterToGlyphMap.Begin(); + mImpl->mVisualModel->GetCharacterToGlyphMap( layoutParameters.charactersToGlyphsBuffer, + 0u, + numberOfCharacters ); + + // Get the glyphs per character table and set into the layout. + Vector glyphsPerCharacter; + glyphsPerCharacter.Resize( numberOfCharacters ); + + layoutParameters.glyphsPerCharacterBuffer = glyphsPerCharacter.Begin(); + mImpl->mVisualModel->GetGlyphsPerCharacterMap( layoutParameters.glyphsPerCharacterBuffer, + 0u, + numberOfCharacters ); + + // Re-layout the text. Reorder those lines with right to left characters. + mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters, + glyphPositions ); + + // Free the allocated memory used to store the conversion table in the bidirectional line info run. + for( Vector::Iterator it = lineBidirectionalInfoRuns.Begin(), + endIt = lineBidirectionalInfoRuns.End(); + it != endIt; + ++it ) + { + BidirectionalLineInfoRun& bidiLineInfo = *it; + + free( bidiLineInfo.visualToLogicalMap ); + } + } + } + // Sets the positions into the model. if( UPDATE_POSITIONS & operations ) { diff --git a/dali-toolkit/internal/text/text-controller.h b/dali-toolkit/internal/text/text-controller.h index b061330..5ffa531 100644 --- a/dali-toolkit/internal/text/text-controller.h +++ b/dali-toolkit/internal/text/text-controller.h @@ -18,18 +18,18 @@ * */ -// INTERNAL INCLUDES -#include -#include -#include - // EXTERNAL INCLUDES +#include #include #include #include #include #include -#include + +// INTERNAL INCLUDES +#include +#include +#include namespace Dali { @@ -70,15 +70,16 @@ private: VALIDATE_FONTS = 0x0004, GET_LINE_BREAKS = 0x0008, GET_WORD_BREAKS = 0x0010, - SHAPE_TEXT = 0x0020, - GET_GLYPH_METRICS = 0x0040, - LAYOUT = 0x0080, - UPDATE_ACTUAL_SIZE = 0x0100, - UPDATE_POSITIONS = 0x0200, - UPDATE_LINES = 0x0400, - REORDER = 0x0800, - ALIGNMENT = 0x1000, - RENDER = 0x2000, + BIDI_INFO = 0x0020, + SHAPE_TEXT = 0x0040, + GET_GLYPH_METRICS = 0x0080, + LAYOUT = 0x0100, + UPDATE_ACTUAL_SIZE = 0x0200, + UPDATE_POSITIONS = 0x0400, + UPDATE_LINES = 0x0800, + REORDER = 0x1000, + ALIGNMENT = 0x2000, + RENDER = 0x4000, ALL_OPERATIONS = 0xFFFF }; diff --git a/dali-toolkit/internal/text/visual-model.cpp b/dali-toolkit/internal/text/visual-model.cpp index 9daa2d1..55d8797 100644 --- a/dali-toolkit/internal/text/visual-model.cpp +++ b/dali-toolkit/internal/text/visual-model.cpp @@ -20,10 +20,10 @@ // EXTERNAL INCLUDES #include - -// INTERNAL INCLUDES #include #include + +// INTERNAL INCLUDES #include namespace Dali @@ -53,6 +53,7 @@ struct VisualModel::Impl Vector mGlyphsToCharacters; ///< For each glyph, the index of the first character. Vector mCharactersToGlyph; ///< For each character, the index of the first glyph. Vector mCharactersPerGlyph; ///< For each glyph, the number of characters that form the glyph. + Vector mGlyphsPerCharacter; ///< For each character, the number of glyphs that are shaped. Vector mGlyphPositions; ///< For each glyph, the position. Vector mLines; ///< The laid out lines. @@ -74,8 +75,9 @@ void VisualModel::SetGlyphs( const GlyphInfo* glyphs, { Vector& modelGlyphs = mImpl->mGlyphs; Vector& modelGlyphsToCharacters = mImpl->mGlyphsToCharacters; - Vector& modelCharactersPerGlyph = mImpl->mCharactersPerGlyph; Vector& modelCharactersToGlyph = mImpl->mCharactersToGlyph; + Vector& modelCharactersPerGlyph = mImpl->mCharactersPerGlyph; + Vector& modelGlyphsPerCharacter = mImpl->mGlyphsPerCharacter; if( 0u == numberOfGlyphs ) { @@ -83,6 +85,7 @@ void VisualModel::SetGlyphs( const GlyphInfo* glyphs, modelGlyphsToCharacters.Clear(); modelCharactersToGlyph.Clear(); modelCharactersPerGlyph.Clear(); + modelGlyphsPerCharacter.Clear(); } else { @@ -106,11 +109,17 @@ void VisualModel::SetGlyphs( const GlyphInfo* glyphs, // Build the characters to glyph conversion table. // 1) Reserve some space for the characters to avoid reallocations. - modelCharactersToGlyph.Reserve( static_cast ( static_cast( numberOfGlyphs ) * 1.3f ) ); + const Length numberOfCharacters = static_cast ( static_cast( numberOfGlyphs ) * 1.3f ); + modelCharactersToGlyph.Reserve( numberOfCharacters ); + modelGlyphsPerCharacter.Reserve( numberOfCharacters ); + + // 2) Traverse the glyphs and set the glyph indices and the glyphs per character. - // 2) Traverse the glyphs and set the glyph indices. + // The number of 'characters per glyph' equal to zero. + Length zeroCharactersPerGlyph = 0u; + + // Index to the glyph. GlyphIndex glyphIndex = 0u; - Length totalNumberOfCharacters = 0u; for( Vector::ConstIterator it = modelCharactersPerGlyph.Begin(), endIt = modelCharactersPerGlyph.End(); it != endIt; @@ -118,10 +127,29 @@ void VisualModel::SetGlyphs( const GlyphInfo* glyphs, { const Length numberOfCharacters = *it; - for( Length index = 0u; index < numberOfCharacters; ++index, ++totalNumberOfCharacters ) + // Set the glyph indices. + for( Length index = 0u; index < numberOfCharacters; ++index ) { modelCharactersToGlyph.PushBack( glyphIndex ); } + + // Set the glyphs per character. + if( 0u == numberOfCharacters ) + { + ++zeroCharactersPerGlyph; + } + else + { + const Length numberOfZeroGlyphsPerCharacter = ( numberOfCharacters - 1u ); + for( Length zeroIndex = 0u; zeroIndex < numberOfZeroGlyphsPerCharacter ; ++zeroIndex ) + { + modelGlyphsPerCharacter.PushBack( 0u ); + } + + modelGlyphsPerCharacter.PushBack( 1u + zeroCharactersPerGlyph ); + + zeroCharactersPerGlyph = 0u; + } } } } @@ -168,6 +196,14 @@ void VisualModel::GetCharacterToGlyphMap( GlyphIndex* characterToGlyphMap, memcpy( characterToGlyphMap, modelCharactersToGlyph.Begin() + characterIndex, numberOfCharacters * sizeof( GlyphIndex ) ); } +void VisualModel::GetGlyphToCharacterMap( CharacterIndex* glyphToCharacter, + GlyphIndex glyphIndex, + Length numberOfGlyphs ) const +{ + const Vector& modelGlyphsToCharacters = mImpl->mGlyphsToCharacters; + memcpy( glyphToCharacter, modelGlyphsToCharacters.Begin() + glyphIndex, numberOfGlyphs * sizeof( CharacterIndex ) ); +} + void VisualModel::GetCharactersPerGlyphMap( Length* charactersPerGlyph, GlyphIndex glyphIndex, Length numberOfGlyphs ) const @@ -176,12 +212,12 @@ void VisualModel::GetCharactersPerGlyphMap( Length* charactersPerGlyph, memcpy( charactersPerGlyph, modelCharactersPerGlyph.Begin() + glyphIndex, numberOfGlyphs * sizeof( Length ) ); } -void VisualModel::GetGlyphToCharacterMap( CharacterIndex* glyphToCharacter, - GlyphIndex glyphIndex, - Length numberOfGlyphs ) const +void VisualModel::GetGlyphsPerCharacterMap( Length* glyphsPerCharacter, + CharacterIndex characterIndex, + Length numberOfCharacters ) const { - const Vector& modelGlyphsToCharacters = mImpl->mGlyphsToCharacters; - memcpy( glyphToCharacter, modelGlyphsToCharacters.Begin() + glyphIndex, numberOfGlyphs * sizeof( CharacterIndex ) ); + const Vector& modelGlyphsPerCharacter = mImpl->mGlyphsPerCharacter; + memcpy( glyphsPerCharacter, modelGlyphsPerCharacter.Begin() + characterIndex, numberOfCharacters * sizeof( Length ) ); } void VisualModel::SetGlyphPositions( const Vector2* glyphPositions, @@ -284,7 +320,7 @@ Length VisualModel::GetNumberOfLines( GlyphIndex glyphIndex, const Vector& modelLines = mImpl->mLines; const GlyphIndex lastGlyphIndex = glyphIndex + numberOfGlyphs; - // Traverse the lines and cound those lines within the range of glyphs. + // Traverse the lines and count those lines within the range of glyphs. for( Vector::ConstIterator it = modelLines.Begin(), endIt = modelLines.End(); it != endIt; diff --git a/dali-toolkit/internal/text/visual-model.h b/dali-toolkit/internal/text/visual-model.h index 9593fd8..1a7120a 100644 --- a/dali-toolkit/internal/text/visual-model.h +++ b/dali-toolkit/internal/text/visual-model.h @@ -18,9 +18,11 @@ * */ -// INTERNAL INCLUDES +// EXTERNAL INCLUDES #include #include + +// INTERNAL INCLUDES #include namespace Dali @@ -146,6 +148,19 @@ public: Length numberOfCharacters ) const; /** + * Retrieves the whole or part of the glyph to character conversion map. + * + * The size of the buffer needs to be big enough to copy the @p numberOfGlyphs. + * + * @param[out] glyphToCharacter Pointer to a buffer where the conversion map is copied. + * @param[in] glyphIndex Index to the first glyph. + * @param[in] numberOfGlyphs The number of glyphs. + */ + void GetGlyphToCharacterMap( CharacterIndex* glyphToCharacter, + GlyphIndex glyphIndex, + Length numberOfGlyphs ) const; + + /** * Retrieves for each glyph the number of characters the glyph represents. * * @param[out] charactersPerGlyph Pointer to a buffer where the number of characters for each glyph are copied. @@ -157,17 +172,15 @@ public: Length numberOfGlyphs ) const; /** - * Retrieves the whole or part of the glyph to character conversion map. + * Retrieves for each character the number of glyphs the character is shaped. * - * The size of the buffer needs to be big enough to copy the @p numberOfGlyphs. - * - * @param[out] glyphToCharacter Pointer to a buffer where the conversion map is copied. - * @param[in] glyphIndex Index to the first glyph. - * @param[in] numberOfGlyphs The number of glyphs. + * @param[out] glyphsPerCharacter Pointer to a buffer where the number of glyphs for each character are copied. + * @param[in] characterIndex Index to the first character. + * @param[in] numberOfCharacters The number of characters. */ - void GetGlyphToCharacterMap( CharacterIndex* glyphToCharacter, - GlyphIndex glyphIndex, - Length numberOfGlyphs ) const; + void GetGlyphsPerCharacterMap( Length* glyphsPerCharacter, + CharacterIndex characterIndex, + Length numberOfCharacters ) const; // Position interface