// CLASS HEADER
#include <dali-toolkit/internal/text/logical-model.h>
+// EXTERNAL INCLUDES
+#include <memory.h>
+
// INTERNAL INCLUDES
#include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
#include <dali-toolkit/internal/text/bidirectional-paragraph-info-run.h>
#include <dali-toolkit/internal/text/font-run.h>
#include <dali-toolkit/internal/text/script-run.h>
-// EXTERNAL INCLUDES
-#include <memory.h>
-
namespace Dali
{
namespace Text
{
+/**
+ * @brief caches some temporary values of the GetNumberOfScriptRuns( characterIndex, numberOfCharacters )
+ * operation and the GetNumberOfFontRuns( characterIndex, numberOfCharacters ) as they are going to be
+ * used in the GetScriptRuns() and the GetFontRuns() calls.
+ */
+struct GetRunCache
+{
+ CharacterIndex characterIndex; ///< The character index.
+ Length numberOfCharacters; ///< The number of characters.
+ Length firstRun; ///< Index to the first run.
+ Length numberOfRuns; ///< The number of runs.
+};
struct LogicalModel::Impl
{
- Vector<Character> mText;
- Vector<ScriptRun> mScriptRuns;
- Vector<FontRun> mFontRuns;
- Vector<LineBreakInfo> mLineBreakInfo;
- Vector<WordBreakInfo> mWordBreakInfo;
+ Vector<Character> mText;
+ Vector<ScriptRun> mScriptRuns;
+ Vector<FontRun> mFontRuns;
+ Vector<LineBreakInfo> mLineBreakInfo;
+ Vector<WordBreakInfo> mWordBreakInfo;
+ Vector<BidirectionalParagraphInfoRun> mBidirectionalParagraphInfo;
+
+ Vector<BidirectionalLineInfoRun> mBidirectionalLineInfo;
+ Vector<CharacterIndex> mLogicalToVisualMap; ///< Bidirectional logical to visual conversion table.
+ Vector<CharacterIndex> mVisualToLogicalMap; ///< Bidirectional visual to logical conversion table.
+
+ 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()
return mImpl->mText.Count();
}
-void LogicalModel::GetText( CharacterIndex characterIndex,
- Character* text,
+void LogicalModel::GetText( Character* text,
+ CharacterIndex characterIndex,
Length numberOfCharacters ) const
{
Vector<Character>& modelText = mImpl->mText;
return mImpl->mText[characterIndex];
}
+void LogicalModel::ReplaceText( CharacterIndex characterIndex,
+ Length numberOfCharactersToRemove,
+ const Character* const text,
+ Length numberOfCharactersToInsert )
+{
+}
+
void LogicalModel::SetScripts( const ScriptRun* const scripts,
Length numberOfRuns )
{
scriptRuns.Resize( numberOfRuns );
memcpy( scriptRuns.Begin(), scripts, numberOfRuns * sizeof( ScriptRun ) );
}
+
+ mImpl->mGetScriptCache.characterIndex = 0u;
+ mImpl->mGetScriptCache.numberOfCharacters = 0u;
+ mImpl->mGetScriptCache.firstRun = 0u;
+ mImpl->mGetScriptCache.numberOfRuns = 0u;
}
Length LogicalModel::GetNumberOfScriptRuns( CharacterIndex characterIndex,
Length numberOfCharacters ) const
{
- if( ( 0u == characterIndex ) && ( mImpl->mText.Count() == numberOfCharacters ) )
+ GetRunCache& scriptCache = mImpl->mGetScriptCache;
+
+ // Set the character index and the number of characters into the cache.
+ scriptCache.characterIndex = characterIndex;
+ scriptCache.numberOfCharacters = numberOfCharacters;
+
+ if( ( 0u == characterIndex ) &&
+ ( mImpl->mText.Count() == numberOfCharacters ) )
{
- return mImpl->mScriptRuns.Count();
+ scriptCache.firstRun = 0u;
+ scriptCache.numberOfRuns = mImpl->mScriptRuns.Count();
+ return scriptCache.numberOfRuns;
}
- const CharacterIndex charcterEndIndex = characterIndex + numberOfCharacters;
- Length numberOfScriptRuns = 0u;
- bool firstIndexFound = false;
+ // Initialize the number of scripts and the index to the first script.
+ scriptCache.firstRun = 0u;
+ scriptCache.numberOfRuns = 0;
+ bool firstScriptFound = false;
- for( Length index = 0u, length = mImpl->mScriptRuns.Count(); index < length; ++index )
+ const Vector<ScriptRun>& modelScripts = mImpl->mScriptRuns;
+ const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
+
+ // Traverse the scripts and count those scripts within the range of characters.
+ for( Vector<ScriptRun>::ConstIterator it = modelScripts.Begin(),
+ endIt = modelScripts.End();
+ it != endIt;
+ ++it )
{
- const ScriptRun* const scriptRun = mImpl->mScriptRuns.Begin() + index;
+ const ScriptRun& script = *it;
- if( !firstIndexFound &&
- ( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters ) )
+ if( ( script.characterRun.characterIndex + script.characterRun.numberOfCharacters > characterIndex ) &&
+ ( lastCharacterIndex > script.characterRun.characterIndex ) )
{
- // The character index is within this script run.
- // Starts the counter of script runs.
- firstIndexFound = true;
+ firstScriptFound = true;
+ ++scriptCache.numberOfRuns;
+ }
+ else if( lastCharacterIndex <= script.characterRun.characterIndex )
+ {
+ // nothing else to do.
+ break;
}
- if( firstIndexFound )
+ if( !firstScriptFound )
{
- ++numberOfScriptRuns;
- if( scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters > charcterEndIndex )
- {
- // This script run exceeds the given range. The number of scripts can be returned.
- return numberOfScriptRuns;
- }
+ ++scriptCache.firstRun;
}
}
- return numberOfScriptRuns;
+ return scriptCache.numberOfRuns;
}
void LogicalModel::GetScriptRuns( ScriptRun* scriptRuns,
CharacterIndex characterIndex,
Length numberOfCharacters ) const
{
- // A better implementation can cache the first script run and the number of then when the GetNumberOfScriptRuns() is called.
+ const Vector<ScriptRun>& modelScripts = mImpl->mScriptRuns;
+ GetRunCache& scriptCache = mImpl->mGetScriptCache;
- Length numberOfScriptRuns = GetNumberOfScriptRuns( characterIndex,
- numberOfCharacters );
-
- for( Length index = 0u, length = mImpl->mScriptRuns.Count(); index < length; ++index )
+ if( ( characterIndex != scriptCache.characterIndex ) ||
+ ( numberOfCharacters != scriptCache.numberOfCharacters ) )
{
- const ScriptRun* const scriptRun = mImpl->mScriptRuns.Begin() + index;
-
- if( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters )
- {
- memcpy( scriptRuns, scriptRun, sizeof( ScriptRun ) * numberOfScriptRuns );
- return;
- }
+ GetNumberOfScriptRuns( characterIndex,
+ numberOfCharacters );
}
+
+ memcpy( scriptRuns, modelScripts.Begin() + scriptCache.firstRun, scriptCache.numberOfRuns * sizeof( ScriptRun ) );
}
Script LogicalModel::GetScript( CharacterIndex characterIndex ) const
return TextAbstraction::UNKNOWN;
}
+void LogicalModel::ReplaceScripts( CharacterIndex characterIndex,
+ Length numberOfCharactersToRemove,
+ const ScriptRun* const scriptRuns,
+ Length numberOfCharactersToInsert )
+{
+}
+
void LogicalModel::SetFonts( const FontRun* const fonts,
Length numberOfRuns )
{
fontRuns.Resize( numberOfRuns );
memcpy( fontRuns.Begin(), fonts, numberOfRuns * sizeof( FontRun ) );
}
+
+ mImpl->mGetFontCache.characterIndex = 0u;
+ mImpl->mGetFontCache.numberOfCharacters = 0u;
+ mImpl->mGetFontCache.firstRun = 0u;
+ mImpl->mGetFontCache.numberOfRuns = 0u;
}
Length LogicalModel::GetNumberOfFontRuns( CharacterIndex characterIndex,
Length numberOfCharacters ) const
{
- if( ( 0u == characterIndex ) && ( mImpl->mText.Count() == numberOfCharacters ) )
+ GetRunCache& fontCache = mImpl->mGetFontCache;
+
+ // Set the character index and the number of characters into the cache.
+ fontCache.characterIndex = characterIndex;
+ fontCache.numberOfCharacters = numberOfCharacters;
+
+ if( ( 0u == characterIndex ) &&
+ ( mImpl->mText.Count() == numberOfCharacters ) )
{
- return mImpl->mFontRuns.Count();
+ fontCache.firstRun = 0u;
+ fontCache.numberOfRuns = mImpl->mFontRuns.Count();
+ return fontCache.numberOfRuns;
}
- const CharacterIndex charcterEndIndex = characterIndex + numberOfCharacters;
- Length numberOfFontRuns = 0u;
- bool firstIndexFound = false;
+ // Initialize the number of fonts and the index to the first font.
+ fontCache.firstRun = 0u;
+ fontCache.numberOfRuns = 0;
+ bool firstFontFound = false;
- for( Length index = 0u, length = mImpl->mFontRuns.Count(); index < length; ++index )
+ const Vector<FontRun>& modelFonts = mImpl->mFontRuns;
+ const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
+
+ // Traverse the fonts and count those fonts within the range of characters.
+ for( Vector<FontRun>::ConstIterator it = modelFonts.Begin(),
+ endIt = modelFonts.End();
+ it != endIt;
+ ++it )
{
- const FontRun* const fontRun = mImpl->mFontRuns.Begin() + index;
+ const FontRun& font = *it;
- if( !firstIndexFound &&
- ( characterIndex < fontRun->characterRun.characterIndex + fontRun->characterRun.numberOfCharacters ) )
+ if( ( font.characterRun.characterIndex + font.characterRun.numberOfCharacters > characterIndex ) &&
+ ( characterIndex + numberOfCharacters > font.characterRun.characterIndex ) )
{
- // The character index is within this font run.
- // Starts the counter of font runs.
- firstIndexFound = true;
+ firstFontFound = true;
+ ++fontCache.numberOfRuns;
+ }
+ else if( lastCharacterIndex <= font.characterRun.characterIndex )
+ {
+ // nothing else to do.
+ break;
}
- if( firstIndexFound )
+ if( !firstFontFound )
{
- ++numberOfFontRuns;
- if( fontRun->characterRun.characterIndex + fontRun->characterRun.numberOfCharacters > charcterEndIndex )
- {
- // This font run exceeds the given range. The number of fonts can be returned.
- return numberOfFontRuns;
- }
+ ++fontCache.firstRun;
}
}
- return numberOfFontRuns;
+ return fontCache.numberOfRuns;
}
void LogicalModel::GetFontRuns( FontRun* fontRuns,
CharacterIndex characterIndex,
Length numberOfCharacters ) const
{
- // A better implementation can cache the first font run and the number of then when the GetNumberOfFontRuns() is called.
+ const Vector<FontRun>& modelFonts = mImpl->mFontRuns;
+ GetRunCache& fontCache = mImpl->mGetFontCache;
- Length numberOfFontRuns = GetNumberOfFontRuns( characterIndex,
- numberOfCharacters );
-
- for( Length index = 0u, length = mImpl->mFontRuns.Count(); index < length; ++index )
+ if( ( characterIndex != fontCache.characterIndex ) ||
+ ( numberOfCharacters != fontCache.numberOfCharacters ) )
{
- const FontRun* const fontRun = mImpl->mFontRuns.Begin() + index;
-
- if( characterIndex < fontRun->characterRun.characterIndex + fontRun->characterRun.numberOfCharacters )
- {
- memcpy( fontRuns, fontRun, sizeof( FontRun ) * numberOfFontRuns );
- return;
- }
+ GetNumberOfFontRuns( characterIndex,
+ numberOfCharacters );
}
+
+ memcpy( fontRuns, modelFonts.Begin() + fontCache.firstRun, fontCache.numberOfRuns * sizeof( FontRun ) );
}
FontId LogicalModel::GetFont( CharacterIndex characterIndex ) const
return 0u;
}
+void LogicalModel::ReplaceFonts( CharacterIndex characterIndex,
+ Length numberOfCharactersToRemove,
+ const FontRun* const fontRuns,
+ Length numberOfCharactersToInsert )
+{
+}
+
void LogicalModel::SetLineBreakInfo( const LineBreakInfo* const lineBreakInfo,
Length length )
{
return *( mImpl->mLineBreakInfo.Begin() + characterIndex );
}
+void LogicalModel::ReplaceLineBreakInfo( CharacterIndex characterIndex,
+ Length numberOfItemsToRemove,
+ const LineBreakInfo* const lineBreakInfo,
+ Length numberOfItemsToInsert )
+{
+}
+
void LogicalModel::SetWordBreakInfo( const WordBreakInfo* const wordBreakInfo,
Length length )
{
return *( mImpl->mWordBreakInfo.Begin() + characterIndex );
}
+void LogicalModel::ReplaceWordBreakInfo( CharacterIndex characterIndex,
+ Length numberOfItemsToRemove,
+ const WordBreakInfo* const wordBreakInfo,
+ Length numberOfItemsToInsert )
+{
+}
+
void LogicalModel::SetBidirectionalInfo( const BidirectionalParagraphInfoRun* const bidirectionalInfo,
Length numberOfRuns )
{
+ Vector<BidirectionalParagraphInfoRun>& 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<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
+
+ // Traverse the bidirectional paragraph info and count those bidi paragraphs within the range of characters.
+ for( Vector<BidirectionalParagraphInfoRun>::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<BidirectionalParagraphInfoRun>& 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 ReplaceBidirectionalInfo( CharacterIndex characterIndex,
+ Length numberOfCharactersToRemove,
+ const BidirectionalParagraphInfoRun* const bidirectionalInfo,
+ Length numberOfCharactersToInsert )
+{
}
void LogicalModel::GetCharacterDirections( CharacterDirection* directions,
void LogicalModel::SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo,
Length numberOfRuns )
{
+ Vector<CharacterIndex>& modelVisualToLogicalMap = mImpl->mVisualToLogicalMap;
+ Vector<CharacterIndex>& 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;
+ }
+ }
+}
+
+void LogicalModel::ReplaceVisualToLogicalMap( CharacterIndex characterIndex,
+ Length numberOfCharactersToRemove,
+ const BidirectionalLineInfoRun* const bidirectionalInfo,
+ Length numberOfCharactersToInsert )
+{
}
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()