/*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// EXTERNAL INCLUDES
#include <memory.h>
-#include <dali/public-api/math/vector2.h>
namespace Dali
{
return VisualModelPtr( new VisualModel() );
}
-void VisualModel::SetGlyphs( const GlyphInfo* glyphs,
- const CharacterIndex* characterIndices,
- const Length* charactersPerGlyph,
- Length numberOfGlyphs )
+void VisualModel::CreateCharacterToGlyphTable( CharacterIndex startIndex,
+ GlyphIndex startGlyphIndex,
+ Length numberOfCharacters )
{
- if( 0u == numberOfGlyphs )
+ if( 0u == numberOfCharacters )
{
- mGlyphs.Clear();
- mGlyphsToCharacters.Clear();
- mCharactersToGlyph.Clear();
- mCharactersPerGlyph.Clear();
- mGlyphsPerCharacter.Clear();
+ // Nothing to do.
+ return;
}
- else
- {
- if( NULL != glyphs )
- {
- mGlyphs.Resize( numberOfGlyphs );
- memcpy( mGlyphs.Begin(), glyphs, numberOfGlyphs * sizeof( GlyphInfo ) );
- }
- if( NULL != characterIndices )
- {
- mGlyphsToCharacters.Resize( numberOfGlyphs );
- memcpy( mGlyphsToCharacters.Begin(), characterIndices, numberOfGlyphs * sizeof( CharacterIndex ) );
- }
+ DALI_ASSERT_DEBUG( mGlyphsPerCharacter.Count() != 0u );
- if( NULL != charactersPerGlyph )
- {
- mCharactersPerGlyph.Resize( numberOfGlyphs );
- memcpy( mCharactersPerGlyph.Begin(), charactersPerGlyph, numberOfGlyphs * sizeof( Length ) );
+ // Get the total number of characters.
+ const Length totalNumberOfCharacters = ( 0u == mGlyphsToCharacters.Count() ) ? 0u : *( mGlyphsToCharacters.End() - 1u ) + *( mCharactersPerGlyph.End() - 1u ); // Index to the first character + the number of characters that form the last glyph.
- // Build the characters to glyph conversion table.
- CreateCharacterToGlyphTable();
+ // Whether the current buffer is being updated or is set from scratch.
+ const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
- // Build the glyphs per character table.
- CreateGlyphsPerCharacterTable();
- }
- }
-}
+ Vector<GlyphIndex> newCharactersToGlyph;
+ GlyphIndex* charactersToGlyphBuffer = NULL;
-void VisualModel::CreateCharacterToGlyphTable( Length numberOfCharacters )
-{
- // 1) Reserve some space for the characters to avoid reallocations.
- if( 0u == numberOfCharacters )
+ // 1) Reserve some space for the glyph indices to avoid reallocations.
+ if( updateCurrentBuffer )
+ {
+ newCharactersToGlyph.Resize( numberOfCharacters );
+ charactersToGlyphBuffer = newCharactersToGlyph.Begin();
+ }
+ else
{
- // If no number of characters is given, just set something sensible to avoid reallocations.
- numberOfCharacters = static_cast<Length> ( static_cast<float>( mGlyphs.Count() ) * 1.3f );
+ mCharactersToGlyph.Resize( numberOfCharacters );
+ charactersToGlyphBuffer = mCharactersToGlyph.Begin() + startIndex;
}
- mCharactersToGlyph.Reserve( numberOfCharacters );
+
+ const Length* const glyphsPerCharacterBuffer = mGlyphsPerCharacter.Begin();
// 2) Traverse the glyphs and set the glyph indices per character.
// Index to the glyph.
- GlyphIndex glyphIndex = 0u;
- for( Vector<Length>::ConstIterator it = mCharactersPerGlyph.Begin(),
+ GlyphIndex glyphIndex = startGlyphIndex;
+ CharacterIndex characterIndex = startIndex;
+ const CharacterIndex lastCharacterIndexPlusOne = startIndex + numberOfCharacters;
+ for( Vector<Length>::ConstIterator it = mCharactersPerGlyph.Begin() + glyphIndex,
endIt = mCharactersPerGlyph.End();
- it != endIt;
- ++it, ++glyphIndex )
+ ( it != endIt ) && ( characterIndex < lastCharacterIndexPlusOne );
+ ++it )
{
const Length numberOfCharactersPerGlyph = *it;
+ Length numberOfGlyphs = 0u;
// Set the glyph indices.
- for( Length index = 0u; index < numberOfCharactersPerGlyph; ++index )
+ for( Length index = 0u; index < numberOfCharactersPerGlyph; ++index, ++characterIndex )
+ {
+ *charactersToGlyphBuffer = glyphIndex;
+ numberOfGlyphs += *( glyphsPerCharacterBuffer + characterIndex );
+ ++charactersToGlyphBuffer;
+ }
+ glyphIndex += numberOfGlyphs;
+ }
+
+ // If the character to glyph buffer is updated, it needs to be inserted in the model.
+ if( updateCurrentBuffer )
+ {
+ // Update the indices.
+ const Length numberOfGlyphs = glyphIndex - startGlyphIndex;
+ for( Vector<Length>::Iterator it = mCharactersToGlyph.Begin() + startIndex,
+ endIt = mCharactersToGlyph.End();
+ it != endIt;
+ ++it )
{
- mCharactersToGlyph.PushBack( glyphIndex );
+ *it += numberOfGlyphs;
}
+
+ mCharactersToGlyph.Insert( mCharactersToGlyph.Begin() + startIndex,
+ newCharactersToGlyph.Begin(),
+ newCharactersToGlyph.End() );
+
}
}
-void VisualModel::CreateGlyphsPerCharacterTable( Length numberOfCharacters )
+void VisualModel::CreateGlyphsPerCharacterTable( CharacterIndex startIndex,
+ GlyphIndex startGlyphIndex,
+ Length numberOfCharacters )
{
- // 1) Reserve some space for the characters to avoid reallocations.
if( 0u == numberOfCharacters )
{
- // If no number of characters is given, just set something sensible to avoid reallocations.
- numberOfCharacters = static_cast<Length> ( static_cast<float>( mGlyphs.Count() ) * 1.3f );
+ // Nothing to do.
+ return;
+ }
+
+ // Get the total number of characters.
+ const Length totalNumberOfCharacters = ( 0u == mGlyphsToCharacters.Count() ) ? 0u : *( mGlyphsToCharacters.End() - 1u ) + *( mCharactersPerGlyph.End() - 1u ); // Index to the first character + the number of characters that form the last glyph.
+
+ // Whether the current buffer is being updated or is set from scratch.
+ const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
+
+ Vector<Length> newGlyphsPerCharacter;
+ Length* glyphsPerCharacterBuffer = NULL;
+
+ // 1) Reserve some space for the glyphs per character to avoid reallocations.
+ if( updateCurrentBuffer )
+ {
+ newGlyphsPerCharacter.Resize( numberOfCharacters );
+ glyphsPerCharacterBuffer = newGlyphsPerCharacter.Begin();
+ }
+ else
+ {
+ mGlyphsPerCharacter.Resize( numberOfCharacters );
+ glyphsPerCharacterBuffer = mGlyphsPerCharacter.Begin() + startIndex;
}
- mCharactersToGlyph.Reserve( numberOfCharacters );
// 2) Traverse the glyphs and set the number of glyphs per character.
+ Length traversedCharacters = 0;
+
// The number of 'characters per glyph' equal to zero.
Length zeroCharactersPerGlyph = 0u;
- for( Vector<Length>::ConstIterator it = mCharactersPerGlyph.Begin(),
+ for( Vector<Length>::ConstIterator it = mCharactersPerGlyph.Begin() + startGlyphIndex,
endIt = mCharactersPerGlyph.End();
- it != endIt;
+ ( it != endIt ) && ( traversedCharacters < numberOfCharacters );
++it )
{
const Length numberOfCharactersPerGlyph = *it;
+ traversedCharacters += numberOfCharactersPerGlyph;
// Set the glyphs per character.
if( 0u == numberOfCharactersPerGlyph )
else
{
const Length numberOfZeroGlyphsPerCharacter = ( numberOfCharactersPerGlyph - 1u );
- for( Length zeroIndex = 0u; zeroIndex < numberOfZeroGlyphsPerCharacter ; ++zeroIndex )
+ for( Length zeroIndex = 0u; zeroIndex < numberOfZeroGlyphsPerCharacter; ++zeroIndex )
{
- mGlyphsPerCharacter.PushBack( 0u );
+ *glyphsPerCharacterBuffer = 0u;
+
+ // Point to the next position in the buffer.
+ ++glyphsPerCharacterBuffer;
}
- mGlyphsPerCharacter.PushBack( 1u + zeroCharactersPerGlyph );
+ *glyphsPerCharacterBuffer = zeroCharactersPerGlyph + 1u;
zeroCharactersPerGlyph = 0u;
+
+ // Point to the next position in the buffer.
+ ++glyphsPerCharacterBuffer;
}
}
-}
-Length VisualModel::GetNumberOfGlyphs() const
-{
- return mGlyphs.Count();
+ // If the glyphs per character buffer is updated, it needs to be inserted in the model.
+ if( updateCurrentBuffer )
+ {
+ mGlyphsPerCharacter.Insert( mGlyphsPerCharacter.Begin() + startIndex,
+ newGlyphsPerCharacter.Begin(),
+ newGlyphsPerCharacter.End() );
+ }
}
void VisualModel::GetGlyphs( GlyphInfo* glyphs,
memcpy( glyphs, mGlyphs.Begin() + glyphIndex, numberOfGlyphs * sizeof( GlyphInfo ) );
}
-const GlyphInfo& VisualModel::GetGlyphInfo( GlyphIndex glyphIndex ) const
+void VisualModel::GetGlyphPositions( Vector2* glyphPositions,
+ GlyphIndex glyphIndex,
+ Length numberOfGlyphs ) const
{
- return mGlyphs[glyphIndex];
+ memcpy( glyphPositions, mGlyphPositions.Begin() + glyphIndex, numberOfGlyphs * sizeof( Vector2 ) );
}
-void VisualModel::ReplaceGlyphs( GlyphIndex glyphIndex,
- Length numberOfGlyphsToRemove,
- const GlyphInfo* const glyphs,
- const Length* const numberOfCharacters,
- Length numberOfGlyphsToInsert )
+void VisualModel::GetNumberOfLines( GlyphIndex glyphIndex,
+ Length numberOfGlyphs,
+ LineIndex& firstLine,
+ Length& numberOfLines ) const
{
+ // Initialize the number of lines and the first line.
+ firstLine = 0u;
+ numberOfLines = 0u;
+ bool firstLineFound = false;
+
+ const GlyphIndex lastGlyphIndex = glyphIndex + numberOfGlyphs;
+
+ // Traverse the lines and count those lines within the range of glyphs.
+ for( Vector<LineRun>::ConstIterator it = mLines.Begin(),
+ endIt = mLines.End();
+ it != endIt;
+ ++it )
+ {
+ const LineRun& line = *it;
+
+ if( ( line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs > glyphIndex ) &&
+ ( lastGlyphIndex > line.glyphRun.glyphIndex ) )
+ {
+ firstLineFound = true;
+ ++numberOfLines;
+ }
+ else if( lastGlyphIndex <= line.glyphRun.glyphIndex )
+ {
+ // nothing else to do.
+ break;
+ }
+
+ if( !firstLineFound )
+ {
+ ++firstLine;
+ }
+ }
}
-CharacterIndex VisualModel::GetCharacterIndex( GlyphIndex glyphIndex ) const
+void VisualModel::GetLinesOfGlyphRange( LineRun* lines,
+ GlyphIndex glyphIndex,
+ Length numberOfGlyphs ) const
{
- return mGlyphsToCharacters[glyphIndex];
+ LineIndex firstLine = 0u;
+ Length numberOfLines = 0u;
+
+ GetNumberOfLines( glyphIndex,
+ numberOfGlyphs,
+ firstLine,
+ numberOfLines );
+
+ memcpy( lines, mLines.Begin() + firstLine, numberOfLines * sizeof( LineRun ) );
}
-Length VisualModel::GetCharactersPerGlyph( GlyphIndex glyphIndex ) const
+LineIndex VisualModel::GetLineOfCharacter( CharacterIndex characterIndex )
{
- return mCharactersPerGlyph[glyphIndex];
+ // 1) Check line is empty or not.
+ if( mLines.Empty() )
+ {
+ return 0u;
+ }
+
+ // 2) Check in the cached line.
+ const LineRun& lineRun = *( mLines.Begin() + mCachedLineIndex );
+ if( ( lineRun.characterRun.characterIndex <= characterIndex ) &&
+ ( characterIndex < lineRun.characterRun.characterIndex + lineRun.characterRun.numberOfCharacters ) )
+ {
+ return mCachedLineIndex;
+ }
+
+ // 3) Is not in the cached line. Check in the other lines.
+ LineIndex index = characterIndex < lineRun.characterRun.characterIndex ? 0u : mCachedLineIndex + 1u;
+
+ for( Vector<LineRun>::ConstIterator it = mLines.Begin() + index,
+ endIt = mLines.End();
+ it != endIt;
+ ++it, ++index )
+ {
+ const LineRun& lineRun = *it;
+
+ if( characterIndex < lineRun.characterRun.characterIndex + lineRun.characterRun.numberOfCharacters )
+ {
+ mCachedLineIndex = index;
+ break;
+ }
+ }
+
+ return index;
}
-GlyphIndex VisualModel::GetGlyphIndex( CharacterIndex characterIndex ) const
+void VisualModel::GetUnderlineRuns( GlyphRun* underlineRuns,
+ UnderlineRunIndex index,
+ Length numberOfRuns ) const
{
- return mCharactersToGlyph[characterIndex];
+ memcpy( underlineRuns,
+ mUnderlineRuns.Begin() + index,
+ numberOfRuns * sizeof( GlyphRun ) );
}
-void VisualModel::GetCharacterToGlyphMap( GlyphIndex* characterToGlyphMap,
- CharacterIndex characterIndex,
- Length numberOfCharacters ) const
+void VisualModel::SetNaturalSize( const Vector2& size )
{
- memcpy( characterToGlyphMap, mCharactersToGlyph.Begin() + characterIndex, numberOfCharacters * sizeof( GlyphIndex ) );
+ mNaturalSize = size;
}
-void VisualModel::GetGlyphToCharacterMap( CharacterIndex* glyphToCharacter,
- GlyphIndex glyphIndex,
- Length numberOfGlyphs ) const
+const Vector2& VisualModel::GetNaturalSize() const
{
- memcpy( glyphToCharacter, mGlyphsToCharacters.Begin() + glyphIndex, numberOfGlyphs * sizeof( CharacterIndex ) );
+ return mNaturalSize;
}
-void VisualModel::GetCharactersPerGlyphMap( Length* charactersPerGlyph,
- GlyphIndex glyphIndex,
- Length numberOfGlyphs ) const
+void VisualModel::SetLayoutSize( const Vector2& size )
{
- memcpy( charactersPerGlyph, mCharactersPerGlyph.Begin() + glyphIndex, numberOfGlyphs * sizeof( Length ) );
+ mLayoutSize = size;
}
-void VisualModel::GetGlyphsPerCharacterMap( Length* glyphsPerCharacter,
- CharacterIndex characterIndex,
- Length numberOfCharacters ) const
+const Vector2& VisualModel::GetLayoutSize() const
{
- memcpy( glyphsPerCharacter, mGlyphsPerCharacter.Begin() + characterIndex, numberOfCharacters * sizeof( Length ) );
+ return mLayoutSize;
}
-void VisualModel::SetGlyphPositions( const Vector2* glyphPositions,
- Length numberOfGlyphs )
+void VisualModel::SetTextColor( const Vector4& textColor )
{
- if( 0u == numberOfGlyphs )
- {
- mGlyphPositions.Clear();
- }
- else
+ mTextColor = textColor;
+
+ if ( !mUnderlineColorSet )
{
- mGlyphPositions.Resize( numberOfGlyphs );
- memcpy( mGlyphPositions.Begin(), glyphPositions, numberOfGlyphs * sizeof( Vector2 ) );
+ mUnderlineColor = textColor;
}
}
-Length VisualModel::GetNumberOfGlyphPositions() const
+void VisualModel::SetShadowOffset( const Vector2& shadowOffset )
{
- return mGlyphPositions.Count();
+ mShadowOffset = shadowOffset;
}
-void VisualModel::GetGlyphPositions( Vector2* glyphPositions,
- GlyphIndex glyphIndex,
- Length numberOfGlyphs ) const
+void VisualModel::SetShadowColor( const Vector4& shadowColor )
{
- memcpy( glyphPositions, mGlyphPositions.Begin() + glyphIndex, numberOfGlyphs * sizeof( Vector2 ) );
+ mShadowColor = shadowColor;
}
-const Vector2& VisualModel::GetGlyphPosition( GlyphIndex glyphIndex ) const
+void VisualModel::SetShadowBlurRadius( const float& shadowBlurRadius )
{
- return *( mGlyphPositions.Begin() + glyphIndex );
+ mShadowBlurRadius = shadowBlurRadius;
}
-void VisualModel::ReplaceGlyphPositions( GlyphIndex glyphIndex,
- Length numberOfGlyphsToRemove,
- const Vector2* const positions,
- Length numberOfGlyphsToInsert )
+void VisualModel::SetUnderlineColor( const Vector4& color )
{
+ mUnderlineColor = color;
+ mUnderlineColorSet = true;
}
-void VisualModel::SetLines( const LineRun* const lines,
- Length numberOfLines )
+void VisualModel::SetOutlineColor( const Vector4& color )
{
- if( 0u == numberOfLines )
- {
- mLines.Clear();
- }
- else
- {
- mLines.Resize( numberOfLines );
- memcpy( mLines.Begin(), lines, numberOfLines * sizeof( LineRun ) );
- }
+ mOutlineColor = color;
}
-Length VisualModel::GetNumberOfLines() const
+void VisualModel::SetUnderlineEnabled( bool enabled )
{
- return mLines.Count();
+ mUnderlineEnabled = enabled;
}
-void VisualModel::GetLines( LineRun* lines,
- LineIndex lineIndex,
- Length numberOfLines ) const
+void VisualModel::SetUnderlineHeight( float height )
{
- memcpy( lines, mLines.Begin() + lineIndex, numberOfLines * sizeof( LineRun ) );
+ mUnderlineHeight = height;
}
-void VisualModel::GetNumberOfLines( GlyphIndex glyphIndex,
- Length numberOfGlyphs,
- LineIndex& firstLine,
- Length& numberOfLines ) const
+void VisualModel::SetOutlineWidth( uint16_t width )
{
- // Initialize the number of lines and the first line.
- firstLine = 0u;
- numberOfLines = 0u;
- bool firstLineFound = false;
+ mOutlineWidth = width;
+}
- const GlyphIndex lastGlyphIndex = glyphIndex + numberOfGlyphs;
+void VisualModel::SetBackgroundColor( const Vector4& color )
+{
+ mBackgroundColor = color;
+}
- // Traverse the lines and count those lines within the range of glyphs.
- for( Vector<LineRun>::ConstIterator it = mLines.Begin(),
- endIt = mLines.End();
- it != endIt;
- ++it )
- {
- const LineRun& line = *it;
+void VisualModel::SetBackgroundEnabled( bool enabled )
+{
+ mBackgroundEnabled = enabled;
+}
- if( ( line.glyphIndex + line.numberOfGlyphs > glyphIndex ) &&
- ( lastGlyphIndex > line.glyphIndex ) )
- {
- firstLineFound = true;
- ++numberOfLines;
- }
- else if( lastGlyphIndex <= line.glyphIndex )
- {
- // nothing else to do.
- break;
- }
+const Vector4& VisualModel::GetTextColor() const
+{
+ return mTextColor;
+}
- if( !firstLineFound )
- {
- ++firstLine;
- }
- }
+const Vector2& VisualModel::GetShadowOffset() const
+{
+ return mShadowOffset;
}
-void VisualModel::GetLinesOfGlyphRange( LineRun* lines,
- GlyphIndex glyphIndex,
- Length numberOfGlyphs ) const
+const Vector4& VisualModel::GetShadowColor() const
{
- LineIndex firstLine = 0u;
- Length numberOfLines = 0u;
+ return mShadowColor;
+}
- GetNumberOfLines( glyphIndex,
- numberOfGlyphs,
- firstLine,
- numberOfLines );
+const float& VisualModel::GetShadowBlurRadius() const
+{
+ return mShadowBlurRadius;
+}
- memcpy( lines, mLines.Begin() + firstLine, numberOfLines * sizeof( LineRun ) );
+const Vector4& VisualModel::GetUnderlineColor() const
+{
+ return mUnderlineColor;
}
-void VisualModel::ReplaceLines( GlyphIndex glyphIndex,
- Length numberOfGlyphsToRemove,
- const LineRun* const lines,
- Length numberOfGlyphsToInsert )
+const Vector4& VisualModel::GetOutlineColor() const
{
+ return mOutlineColor;
}
-void VisualModel::SetNaturalSize( const Vector2& size )
+bool VisualModel::IsUnderlineEnabled() const
{
- mNaturalSize = size;
+ return mUnderlineEnabled;
}
-const Vector2& VisualModel::GetNaturalSize() const
+float VisualModel::GetUnderlineHeight() const
{
- return mNaturalSize;
+ return mUnderlineHeight;
+}
+
+uint16_t VisualModel::GetOutlineWidth() const
+{
+ return mOutlineWidth;
+}
+
+const Vector4& VisualModel::GetBackgroundColor() const
+{
+ return mBackgroundColor;
+}
+
+bool VisualModel::IsBackgroundEnabled() const
+{
+ return mBackgroundEnabled;
}
-void VisualModel::SetActualSize( const Vector2& size )
+Length VisualModel::GetNumberOfUnderlineRuns() const
{
- mActualSize = size;
+ return mUnderlineRuns.Count();
}
-const Vector2& VisualModel::GetActualSize() const
+void VisualModel::ClearCaches()
{
- return mActualSize;
+ mCachedLineIndex = 0u;
}
VisualModel::~VisualModel()
}
VisualModel::VisualModel()
+: mGlyphs(),
+ mGlyphsToCharacters(),
+ mCharactersToGlyph(),
+ mCharactersPerGlyph(),
+ mGlyphsPerCharacter(),
+ mGlyphPositions(),
+ mLines(),
+ mTextColor( Color::BLACK ),
+ mShadowColor( Color::BLACK ),
+ mUnderlineColor( Color::BLACK ),
+ mOutlineColor( Color::WHITE ),
+ mBackgroundColor( Color::TRANSPARENT ),
+ mControlSize(),
+ mShadowOffset(),
+ mUnderlineHeight( 0.0f ),
+ mShadowBlurRadius( 0.0f ),
+ mOutlineWidth( 0u ),
+ mNaturalSize(),
+ mLayoutSize(),
+ mCachedLineIndex( 0u ),
+ mUnderlineEnabled( false ),
+ mUnderlineColorSet( false ),
+ mBackgroundEnabled( false )
{
}