}
-int UtcDaliTextControllerMaxLengthSetText(void)
-{
- tet_infoline(" UtcDaliTextControllerMaxLengthSetText");
- ToolkitTestApplication application;
-
- // Creates a text controller.
- ControllerPtr controller = Controller::New();
-
- ConfigureTextLabel(controller);
-
- const Length MAX_TEXT_LENGTH = 1024u * 32u;
-
- // make over length world
- int maxLength = (1024u * 32u) + 10u;
- char world[maxLength];
- for( int i = 0; i < maxLength; i++ )
- {
- world[i] = 'a';
- }
-
- // Set the text
- std::string text(world);
- controller->SetText( text );
-
- // Perform a relayout
- const Size size( Dali::Stage::GetCurrent().GetSize() );
- controller->Relayout(size);
-
- // check text length
- controller->GetText( text );
- Length textSize = text.size();
-
- DALI_TEST_EQUALS( MAX_TEXT_LENGTH, textSize, TEST_LOCATION );
-
- END_TEST;
-}
-
-int UtcDaliTextControllerDirectionCoverage(void)
-{
- tet_infoline(" UtcDaliTextControllerDirectionCoverage");
- ToolkitTestApplication application;
-
- // Creates a text controller.
- ControllerPtr controller = Controller::New();
-
- ConfigureTextLabel(controller);
-
- controller->SetMatchSystemLanguageDirection( true );
- controller->SetLayoutDirection( Dali::LayoutDirection::RIGHT_TO_LEFT );
-
- // Perform a relayout
- const Size size( Dali::Stage::GetCurrent().GetSize() );
- controller->Relayout(size);
-
- tet_result(TET_PASS);
-
- END_TEST;
-}
-
int UtcDaliTextControllerRemoveTextChangeEventData(void)
{
tet_infoline(" UtcDaliTextControllerRemoveTextChangeEventData");
#include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
#include <dali/devel-api/text-abstraction/bitmap-font.h>
#include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
#include <dali-toolkit/devel-api/text/bitmap-font.h>
+#include <dali-toolkit/devel-api/text/text-utils-devel.h>
using namespace Dali;
using namespace Toolkit;
label.SetProperty( Actor::Property::LAYOUT_DIRECTION, LayoutDirection::RIGHT_TO_LEFT );
DALI_TEST_EQUALS( label.GetProperty< int >( Actor::Property::LAYOUT_DIRECTION ), static_cast< int >( LayoutDirection::RIGHT_TO_LEFT ), TEST_LOCATION );
+ // Check the line size property
+ DALI_TEST_EQUALS( label.GetProperty<float>( DevelTextLabel::Property::MIN_LINE_SIZE ), 0.0f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+ label.SetProperty( DevelTextLabel::Property::MIN_LINE_SIZE, 50.f );
+ DALI_TEST_EQUALS( label.GetProperty<float>( DevelTextLabel::Property::MIN_LINE_SIZE ), 50.0f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+
application.SendNotification();
application.Render();
END_TEST;
}
+
+int UtcDaliToolkitTextlabelMaxTextureSet(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliToolkitTextlabelMaxTextureSet");
+
+ DevelText::BitmapFontDescription fontDescription;
+ fontDescription.name = "Digits";
+ fontDescription.glyphs.push_back( { TEST_RESOURCE_DIR "/fonts/bitmap/u0030.png", ":", 200.f, 0.f } );
+
+ TextAbstraction::BitmapFont bitmapFont;
+ DevelText::CreateBitmapFont( fontDescription, bitmapFont );
+
+ TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+ fontClient.GetFontId( bitmapFont );
+
+ TextLabel label = TextLabel::New();
+ label.SetProperty( TextLabel::Property::FONT_FAMILY, "Digits" );
+ label.SetProperty( TextLabel::Property::ENABLE_MARKUP, true );
+ label.SetProperty( TextLabel::Property::TEXT, ":This is a long sample text made to allow max texture size to be exceeded." );
+ label.SetProperty( TextLabel::Property::POINT_SIZE, 200.f );
+ label.SetProperty( TextLabel::Property::MULTI_LINE, true );
+
+ Property::Map underlineMapSet;
+ underlineMapSet.Clear();
+ underlineMapSet.Insert( "enable", true );
+ underlineMapSet.Insert( "color", Color::RED );
+ underlineMapSet.Insert( "height", 1 );
+ label.SetProperty( TextLabel::Property::UNDERLINE, underlineMapSet );
+ label.SetProperty( Toolkit::TextLabel::Property::TEXT_COLOR, Color::BLUE );
+
+ Stage::GetCurrent().Add( label );
+
+ application.SendNotification();
+ application.Render();
+
+ const int maxTextureSize = Dali::GetMaxTextureSize();
+ // Whether the rendered text is greater than maxTextureSize
+ DALI_TEST_CHECK( label.GetCurrentSize().height > maxTextureSize );
+
+ // Check if the number of renderers is greater than 1.
+ DALI_TEST_CHECK( label.GetRendererCount() > 1u );
+
+ END_TEST;
+}
*/
TEXT_FIT,
+ /**
+ * @brief Sets the height of the line in points.
+ * @details Name "lineSize", type Property::FLOAT.
+ * @note If the font size is larger than the line size, it works with the font size.
+ */
+ MIN_LINE_SIZE,
+
};
} // namespace Property
#include <dali/integration-api/debug.h>
#include <dali/devel-api/scripting/enum-helper.h>
#include <cstring>
+#include <limits>
// INTERNAL INCLUDES
#include <dali-toolkit/internal/text/bidirectional-support.h>
const float RAD_225 = RAD_135 + Math::PI_2; ///< 225 degrees in radians;
const float RAD_270 = 3.f * Math::PI_2; ///< 270 degrees in radians;
const float RAD_315 = RAD_225 + Math::PI_2; ///< 315 degrees in radians;
+const float MAX_INT = std::numeric_limits<int>::max();
DALI_ENUM_TO_STRING_TABLE_BEGIN( LAYOUT_TYPE )
DALI_ENUM_TO_STRING_WITH_SCOPE( DevelText::Layout, SINGLELINE )
DALI_ENUM_TO_STRING_WITH_SCOPE( DevelText::CircularAlignment, END )
DALI_ENUM_TO_STRING_TABLE_END( CIRCULAR_ALIGNMENT_TYPE )
+struct InternalDataModel
+{
+ InternalDataModel( FontClient& fontClient,
+ MetricsPtr metrics,
+ Text::ModelPtr textModel )
+ : fontClient( fontClient ),
+ metrics( metrics ),
+ textModel( textModel ),
+ numberOfCharacters{ 0u },
+ isTextMirrored{ false },
+ numberOfGlyphs{ 0u }
+ {
+ layoutEngine.SetMetrics( metrics );
+ }
+
+ FontClient& fontClient;
+ MetricsPtr metrics;
+ Text::Layout::Engine layoutEngine; ///< The layout engine.
+ Text::ModelPtr textModel; ///< Pointer to the text's model.
+ Vector<ColorBlendingMode> blendingMode; ///< How embedded items and bitmap font glyphs are blended with color text.
+ Vector<bool> isEmoji; ///< Whether the glyph is an emoji.
+
+ Vector<Character> mirroredUtf32Characters; // The utf32Characters Characters but mirrored if there are RTL text.
+
+ Length numberOfCharacters; // The number of characters (not glyphs!).
+ bool isTextMirrored ; // Whether the text has been mirrored.
+
+ Length numberOfGlyphs;
+ Size textLayoutArea;
+};
+
bool GetLayoutEnumeration(const Property::Value& propertyValue, DevelText::Layout::Type& layout)
{
return Scripting::GetEnumerationProperty(propertyValue, LAYOUT_TYPE_TABLE, LAYOUT_TYPE_TABLE_COUNT, layout);
return Scripting::GetEnumerationProperty(propertyValue, CIRCULAR_ALIGNMENT_TYPE_TABLE, CIRCULAR_ALIGNMENT_TYPE_TABLE_COUNT, circularAlignment);
}
-Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout )
+void ShapeTextPreprocess( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters, InternalDataModel& internalDataModel )
{
- if( textParameters.text.empty() )
- {
- Dali::Devel::PixelBuffer pixelBuffer = Dali::Devel::PixelBuffer::New( textParameters.textWidth,
- textParameters.textHeight,
- Dali::Pixel::RGBA8888 );
-
- const unsigned int bufferSize = textParameters.textWidth * textParameters.textHeight * Dali::Pixel::GetBytesPerPixel(Dali::Pixel::RGBA8888);
- unsigned char* buffer = pixelBuffer.GetBuffer();
- memset(buffer, 0, bufferSize);
-
- return pixelBuffer;
- }
MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
- FontClient fontClient = FontClient::Get();
- MetricsPtr metrics;
- Text::Layout::Engine layoutEngine; ///< The layout engine.
- Text::ModelPtr textModel = Text::Model::New(); ///< Pointer to the text's model.
- Vector<ColorBlendingMode> blendingMode; ///< How embedded items and bitmap font glyphs are blended with color text.
- Vector<bool> isEmoji; ///< Whether the glyph is an emoji.
-
- // Use this to access FontClient i.e. to get down-scaled Emoji metrics.
- metrics = Metrics::New( fontClient );
- layoutEngine.SetMetrics( metrics );
-
- TextAbstraction::TextRenderer::Parameters rendererParameters( textModel->mVisualModel->mGlyphs,
- textModel->mVisualModel->mGlyphPositions,
- textModel->mVisualModel->mColors,
- textModel->mVisualModel->mColorIndices,
- blendingMode,
- isEmoji );
+ const uint8_t* utf8 = NULL; // pointer to the first character of the text (encoded in utf8)
+ Length textSize = 0u; // The length of the utf8 string.
- rendererParameters.width = textParameters.textWidth;
- rendererParameters.height = textParameters.textHeight;
- rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888; // @note: At the moment all textures are generated RGBA8888
+ Length& numberOfCharacters = internalDataModel.numberOfCharacters;
+ Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
+ Text::ModelPtr& textModel = internalDataModel.textModel;
Vector<Character>& utf32Characters = textModel->mLogicalModel->mText; // Characters encoded in utf32.
- Vector<Character> mirroredUtf32Characters; // The utf32Characters Characters but mirrored if there are RTL text.
Vector<LineBreakInfo>& lineBreakInfo = textModel->mLogicalModel->mLineBreakInfo; // The line break info.
Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns; // Charactes's script.
Vector<FontDescriptionRun>& fontDescriptionRuns = textModel->mLogicalModel->mFontDescriptionRuns; // Desired font descriptions.
Vector<FontRun>& validFonts = textModel->mLogicalModel->mFontRuns; // Validated fonts.
Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = textModel->mLogicalModel->mBidirectionalParagraphInfo; // The bidirectional info per paragraph.
- //Vector<BidirectionalLineInfoRun>& bidirectionalLineInfo = textModel->mLogicalModel->mBidirectionalLineInfo; // The bidirectional info per line.
Vector<CharacterDirection>& directions = textModel->mLogicalModel->mCharacterDirections; // Character's directions.
Vector<ColorRun>& colorRuns = textModel->mLogicalModel->mColorRuns; // colors of the text.
- Vector<CharacterIndex>& glyphsToCharacters = textModel->mVisualModel->mGlyphsToCharacters; // Glyphs to character map.
- Vector<GlyphIndex>& charactersToGlyph = textModel->mVisualModel->mCharactersToGlyph; // Characters to glyphs map.
- Vector<Length>& charactersPerGlyph = textModel->mVisualModel->mCharactersPerGlyph; // Number of characters per glyph.
- Vector<Length>& glyphsPerCharacter = textModel->mVisualModel->mGlyphsPerCharacter; // The number of glyphs that are shaped.
- Vector<LineRun>& lines = textModel->mVisualModel->mLines; // The laid out lines.
-
- Vector<GlyphIndex> newParagraphGlyphs; // Glyphs for the new paragraph characters.
-
// the default font's description.
FontDescription defaultFontDescription;
PointSize26Dot6 defaultPointSize = FontClient::DEFAULT_POINT_SIZE;
- Length numberOfCharacters = 0u; // The number of characters (not glyphs!).
- bool isTextMirrored = false; // Whether the text has been mirrored.
-
- const uint8_t* utf8 = NULL; // pointer to the first character of the text (encoded in utf8)
- Length textSize = 0u; // The length of the utf8 string.
-
////////////////////////////////////////////////////////////////////////////////
// Process the markup string if the mark-up processor is enabled.
////////////////////////////////////////////////////////////////////////////////
MarkupProcessData markupProcessData( colorRuns,
- fontDescriptionRuns,
- textModel->mLogicalModel->mEmbeddedItems );
+ fontDescriptionRuns,
+ textModel->mLogicalModel->mEmbeddedItems );
if (textParameters.markupEnabled)
{
////////////////////////////////////////////////////////////////////////////////
multilanguageSupport.SetScripts( utf32Characters,
- 0u,
- numberOfCharacters,
- scripts );
+ 0u,
+ numberOfCharacters,
+ scripts );
// Check if there are emojis.
// If there are an RGBA8888 pixel format is needed.
// This paragraph has right to left text. Some characters may need to be mirrored.
// TODO: consider if the mirrored string can be stored as well.
- isTextMirrored = GetMirroredText( utf32Characters,
+ internalDataModel.isTextMirrored = GetMirroredText( utf32Characters,
directions,
bidirectionalInfo,
0u,
numberOfCharacters,
mirroredUtf32Characters );
}
+}
+
+void ShapeText( TextAbstraction::TextRenderer::Parameters& rendererParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel )
+{
+ Vector<GlyphIndex> newParagraphGlyphs; // Glyphs for the new paragraph characters.
+ const Length numberOfCharacters = internalDataModel.numberOfCharacters;
+ const bool isTextMirrored = internalDataModel.isTextMirrored;
+ Text::ModelPtr& textModel = internalDataModel.textModel;
+ const Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
+ FontClient& fontClient = internalDataModel.fontClient;
+ const Vector<Character>& utf32Characters = textModel->mLogicalModel->mText; // Characters encoded in utf32.
+ const Vector<LineBreakInfo>& lineBreakInfo = textModel->mLogicalModel->mLineBreakInfo; // The line break info.
+ const Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns; // Charactes's script.
+ const Vector<FontRun>& validFonts = textModel->mLogicalModel->mFontRuns; // Validated fonts.
+
+ Vector<CharacterIndex>& glyphsToCharacters = textModel->mVisualModel->mGlyphsToCharacters; // Glyphs to character map.
+ Vector<Length>& charactersPerGlyph = textModel->mVisualModel->mCharactersPerGlyph; // Number of characters per glyph.
////////////////////////////////////////////////////////////////////////////////
// Retrieve the glyphs. Text shaping
textModel->mVisualModel->CreateGlyphsPerCharacterTable( 0u, 0u, numberOfCharacters );
textModel->mVisualModel->CreateCharacterToGlyphTable( 0u, 0u, numberOfCharacters );
- const Length numberOfGlyphs = rendererParameters.glyphs.Count();
+ internalDataModel.numberOfGlyphs = rendererParameters.glyphs.Count();
// Once the text has been shaped and the glyphs created it's possible to replace the font id of those glyphs
// that represent an image or an item and create the embedded item layout info.
embeddedItemLayout.PushBack( embeddedInfo );
}
}
+}
- ////////////////////////////////////////////////////////////////////////////////
- // Retrieve the glyph's metrics.
- ////////////////////////////////////////////////////////////////////////////////
+void SetColorSegmentation( const RendererParameters& textParameters, InternalDataModel& internalDataModel )
+{
- metrics->GetGlyphMetrics( rendererParameters.glyphs.Begin(), numberOfGlyphs );
+ Text::ModelPtr& textModel = internalDataModel.textModel;
+ Vector<ColorBlendingMode>& blendingMode = internalDataModel.blendingMode;
+
+ Vector<ColorRun>& colorRuns = textModel->mLogicalModel->mColorRuns; // colors of the text.
+
+ Vector<GlyphIndex>& charactersToGlyph = textModel->mVisualModel->mCharactersToGlyph; // Characters to glyphs map.
+ Vector<Length>& glyphsPerCharacter = textModel->mVisualModel->mGlyphsPerCharacter; // The number of glyphs that are shaped.
////////////////////////////////////////////////////////////////////////////////
// Set the color runs in glyphs.
glyphsPerCharacter,
0u,
0u,
- numberOfCharacters,
+ internalDataModel.numberOfCharacters,
textModel->mVisualModel->mColors,
textModel->mVisualModel->mColorIndices );
textModel->mVisualModel->mColors.Insert( textModel->mVisualModel->mColors.Begin(),textParameters.textColor );
// Set how the embedded items are blended with text color.
- blendingMode.Resize( numberOfGlyphs, textParameters.isTextColorSet ? ColorBlendingMode::MULTIPLY : ColorBlendingMode::NONE );
+ blendingMode.Resize( internalDataModel.numberOfGlyphs, textParameters.isTextColorSet ? ColorBlendingMode::MULTIPLY : ColorBlendingMode::NONE );
if( !textParameters.isTextColorSet )
{
const GlyphIndex glyphIndex = textModel->mVisualModel->mCharactersToGlyph[item.characterIndex];
blendingMode[glyphIndex] = item.colorBlendingMode;
}
+}
+
+void SetEmojiVector( InternalDataModel& internalDataModel )
+{
+ Vector<bool>& isEmoji = internalDataModel.isEmoji;
+ Text::ModelPtr& textModel = internalDataModel.textModel;
+ const Length numberOfGlyphs = internalDataModel.numberOfGlyphs;
+ const Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns; // Charactes's script.
////////////////////////////////////////////////////////////////////////////////
// Set the isEmoji Vector
////////////////////////////////////////////////////////////////////////////////
}
}
}
+}
+
+
+void Align( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters,
+ Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel,
+ const Size& newLayoutSize )
+{
+ Text::Layout::Engine& layoutEngine = internalDataModel.layoutEngine;
+ Text::ModelPtr& textModel = internalDataModel.textModel;
+ const Length numberOfCharacters = internalDataModel.numberOfCharacters;
+ Size& textLayoutArea = internalDataModel.textLayoutArea;
+
+ Vector<LineRun>& lines = textModel->mVisualModel->mLines; // The laid out lines.
////////////////////////////////////////////////////////////////////////////////
- // Layout the text.
+ // Align the text.
////////////////////////////////////////////////////////////////////////////////
- // Sets the alignment
HorizontalAlignment::Type horizontalAlignment = Toolkit::HorizontalAlignment::CENTER;
HorizontalAlignment::Type horizontalCircularAlignment = Toolkit::HorizontalAlignment::CENTER;
VerticalAlignment::Type verticalAlignment = VerticalAlignment::CENTER;
- Layout::Type layout = Layout::SINGLELINE;
CircularAlignment::Type circularAlignment = CircularAlignment::BEGIN;
+ Layout::Type layout = Layout::SINGLELINE;
+
+ // Sets the alignment
Property::Value horizontalAlignmentStr( textParameters.horizontalAlignment );
GetHorizontalAlignmentEnumeration( horizontalAlignmentStr, horizontalAlignment );
horizontalCircularAlignment = horizontalAlignment;
Property::Value verticalAlignmentStr( textParameters.verticalAlignment );
GetVerticalAlignmentEnumeration( verticalAlignmentStr, verticalAlignment );
- Property::Value layoutStr( textParameters.layout );
- GetLayoutEnumeration( layoutStr, layout );
-
Property::Value circularAlignmentStr( textParameters.circularAlignment );
GetCircularAlignmentEnumeration( circularAlignmentStr, circularAlignment );
- // Whether the layout is multi-line.
- const Text::Layout::Engine::Type horizontalLayout = ( Layout::MULTILINE == layout ) ? Text::Layout::Engine::MULTI_LINE_BOX : Text::Layout::Engine::SINGLE_LINE_BOX;
- layoutEngine.SetLayout( horizontalLayout ); // TODO: multi-line.
-
+ Property::Value layoutStr( textParameters.layout );
+ GetLayoutEnumeration( layoutStr, layout );
// Whether the layout is circular.
const bool isCircularTextLayout = (Layout::CIRCULAR == layout);
const bool isClockwise = isCircularTextLayout && ( 0.f < textParameters.incrementAngle );
- // Calculates the max ascender or the max descender.
- // Is used to calculate the radius of the base line of the text.
- float maxAscenderDescender = 0.f;
- if( isCircularTextLayout )
- {
- FontId currentFontId = 0u;
- for( const auto& glyph : rendererParameters.glyphs )
- {
- if( currentFontId != glyph.fontId )
- {
- currentFontId = glyph.fontId;
- FontMetrics metrics;
- fontClient.GetFontMetrics(currentFontId, metrics);
- maxAscenderDescender = std::max( maxAscenderDescender, isClockwise ? metrics.ascender : metrics.descender );
- }
- }
- }
- const unsigned int radius = textParameters.radius - static_cast<unsigned int>( maxAscenderDescender );
-
// Convert CircularAlignment to HorizontalAlignment.
if( isCircularTextLayout )
{
}
}
}
-
- // Set the layout parameters.
- Size textLayoutArea( static_cast<float>( textParameters.textWidth ),
- static_cast<float>( textParameters.textHeight ) );
-
- if( isCircularTextLayout )
- {
- // In a circular layout, the length of the text area depends on the radius.
- rendererParameters.radius = radius;
- textLayoutArea.width = fabs( Radian( Degree( textParameters.incrementAngle ) ) * static_cast<float>( rendererParameters.radius ) );
- }
-
textModel->mHorizontalAlignment = isCircularTextLayout ? horizontalCircularAlignment : horizontalAlignment;
- textModel->mLineWrapMode = LineWrap::WORD;
- textModel->mIgnoreSpacesAfterText = false;
- textModel->mMatchSystemLanguageDirection = false;
- Text::Layout::Parameters layoutParameters( textLayoutArea,
- textModel );
-
- // Resize the vector of positions to have the same size than the vector of glyphs.
- rendererParameters.positions.Resize( numberOfGlyphs );
-
- // Whether the last character is a new paragraph character.
- layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( textToShape[numberOfCharacters - 1u] );
-
- // The initial glyph and the number of glyphs to layout.
- layoutParameters.startGlyphIndex = 0u;
- layoutParameters.numberOfGlyphs = numberOfGlyphs;
- layoutParameters.startLineIndex = 0u;
- layoutParameters.estimatedNumberOfLines = 1u;
- layoutParameters.interGlyphExtraAdvance = 0.f;
-
- // Update the visual model.
- Size newLayoutSize;
- bool isAutoScrollEnabled = false;
- layoutEngine.LayoutText( layoutParameters,
- newLayoutSize,
- textParameters.ellipsisEnabled,
- isAutoScrollEnabled );
-
- ////////////////////////////////////////////////////////////////////////////////
- // Align the text.
- ////////////////////////////////////////////////////////////////////////////////
// Retrieve the line of text to know the direction and the width. @todo multi-line
const LineRun& line = lines[0u];
{
const bool isNeg = textParameters.incrementAngle < 0.f;
const float textWidth = static_cast<float>( rendererParameters.circularWidth );
- angleOffset = ( isNeg ? -0.5f : 0.5f ) * ( textLayoutArea.width - textWidth ) / static_cast<float>( radius );
+ angleOffset = ( isNeg ? -0.5f : 0.5f ) * ( textLayoutArea.width - textWidth ) / static_cast<float>( rendererParameters.radius );
break;
}
case CircularAlignment::END:
{
const bool isNeg = textParameters.incrementAngle < 0.f;
const float textWidth = static_cast<float>( rendererParameters.circularWidth );
- angleOffset = ( isNeg ? -1.f : 1.f ) * ( textLayoutArea.width - textWidth ) / static_cast<float>( radius );
+ angleOffset = ( isNeg ? -1.f : 1.f ) * ( textLayoutArea.width - textWidth ) / static_cast<float>( rendererParameters.radius );
break;
}
}
}
// Calculate the horizontal offset according with the given alignment.
- float alignmentOffset = 0.f;
+ float alignmentOffset = 0.f;
layoutEngine.Align( textLayoutArea,
0u,
numberOfCharacters,
// Update the position of the glyphs with the calculated offsets.
for( auto& position : rendererParameters.positions )
- {
- position.x += line.alignmentOffset;
- position.y = penY;
- }
+ {
+ position.x += line.alignmentOffset;
+ position.y = penY;
+ }
}
// Cairo adds the bearing to the position of the glyph
Dali::TextAbstraction::CircularTextParameters circularTextParameters;
- circularTextParameters.radius = static_cast<double>( radius );
+ circularTextParameters.radius = static_cast<double>( rendererParameters.radius );
circularTextParameters.invRadius = 1.0 / circularTextParameters.radius;
circularTextParameters.beginAngle = static_cast<double>( -rendererParameters.beginAngle + Dali::Math::PI_2 );
circularTextParameters.centerX = 0.5f * static_cast<double>( textParameters.textWidth );
}
}
+}
+
+void Ellipsis( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters,
+ Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel )
+{
+ Text::ModelPtr& textModel = internalDataModel.textModel;
+ FontClient& fontClient = internalDataModel.fontClient;
+
+ Vector<LineRun>& lines = textModel->mVisualModel->mLines; // The laid out lines.
+ Vector<bool>& isEmoji = internalDataModel.isEmoji;
+ const Size textLayoutArea = internalDataModel.textLayoutArea;
////////////////////////////////////////////////////////////////////////////////
// Ellipsis the text.
////////////////////////////////////////////////////////////////////////////////
}
}
}
+}
+Size LayoutText( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters,
+ Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel )
+{
+ ////////////////////////////////////////////////////////////////////////////////
+ // Layout the text.
+ ////////////////////////////////////////////////////////////////////////////////
+ Text::ModelPtr& textModel = internalDataModel.textModel;
+ Text::Layout::Engine& layoutEngine = internalDataModel.layoutEngine;
+ FontClient& fontClient = internalDataModel.fontClient;
+ const Length numberOfGlyphs = internalDataModel.numberOfGlyphs;
+ const bool isTextMirrored = internalDataModel.isTextMirrored;
+ const Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
+ const Length numberOfCharacters = internalDataModel.numberOfCharacters;
+
+ Layout::Type layout = Layout::SINGLELINE;
+
+ Property::Value layoutStr( textParameters.layout );
+ GetLayoutEnumeration( layoutStr, layout );
+
+ // Whether the layout is multi-line.
+ const Text::Layout::Engine::Type horizontalLayout = ( Layout::MULTILINE == layout ) ? Text::Layout::Engine::MULTI_LINE_BOX : Text::Layout::Engine::SINGLE_LINE_BOX;
+ layoutEngine.SetLayout( horizontalLayout ); // TODO: multi-line.
+
+ // Set minimun line size
+ layoutEngine.SetDefaultLineSize( textParameters.minLineSize );
+
+ // Whether the layout is circular.
+ const bool isCircularTextLayout = (Layout::CIRCULAR == layout);
+ const bool isClockwise = isCircularTextLayout && ( 0.f < textParameters.incrementAngle );
+
+ // Calculates the max ascender or the max descender.
+ // Is used to calculate the radius of the base line of the text.
+ float maxAscenderDescender = 0.f;
+ if( isCircularTextLayout )
+ {
+ FontId currentFontId = 0u;
+ for( const auto& glyph : rendererParameters.glyphs )
+ {
+ if( currentFontId != glyph.fontId )
+ {
+ currentFontId = glyph.fontId;
+ FontMetrics metrics;
+ fontClient.GetFontMetrics(currentFontId, metrics);
+ maxAscenderDescender = std::max( maxAscenderDescender, isClockwise ? metrics.ascender : metrics.descender );
+ }
+ }
+ }
+ const unsigned int radius = textParameters.radius - static_cast<unsigned int>( maxAscenderDescender );
+
+ // Set the layout parameters.
+ Size textLayoutArea = Size(static_cast<float>(textParameters.textWidth),
+ static_cast<float>(textParameters.textHeight));
+
+ // padding
+ Extents padding = textParameters.padding;
+ internalDataModel.textLayoutArea = Size(textLayoutArea.x - ( padding.start + padding.end ), textLayoutArea.y - ( padding.top + padding.bottom ) );
+
+
+ if(isCircularTextLayout)
+ {
+ // In a circular layout, the length of the text area depends on the radius.
+ rendererParameters.radius = radius;
+ internalDataModel.textLayoutArea.width = fabs( Radian( Degree( textParameters.incrementAngle ) ) * static_cast<float>( rendererParameters.radius ) );
+ }
+ // Resize the vector of positions to have the same size than the vector of glyphs.
+ rendererParameters.positions.Resize( numberOfGlyphs );
+
+ textModel->mLineWrapMode = LineWrap::WORD;
+ textModel->mIgnoreSpacesAfterText = false;
+ textModel->mMatchSystemLanguageDirection = false;
+ Text::Layout::Parameters layoutParameters( internalDataModel.textLayoutArea,
+ textModel );
+
+
+ // Whether the last character is a new paragraph character.
+ const Vector<Character>& textToShape = isTextMirrored ? mirroredUtf32Characters : textModel->mLogicalModel->mText;
+ layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( textToShape[numberOfCharacters - 1u] );
+
+ // The initial glyph and the number of glyphs to layout.
+ layoutParameters.startGlyphIndex = 0u;
+ layoutParameters.numberOfGlyphs = numberOfGlyphs;
+ layoutParameters.startLineIndex = 0u;
+ layoutParameters.estimatedNumberOfLines = 1u;
+ layoutParameters.interGlyphExtraAdvance = 0.f;
+
+ // Update the visual model.
+ Size newLayoutSize;
+ bool isAutoScrollEnabled = false;
+ layoutEngine.LayoutText( layoutParameters,
+ newLayoutSize,
+ textParameters.ellipsisEnabled,
+ isAutoScrollEnabled );
+
+ return newLayoutSize;
+
+}
+
+
+Devel::PixelBuffer RenderText( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters )
+{
////////////////////////////////////////////////////////////////////////////////
// Render the text.
////////////////////////////////////////////////////////////////////////////////
return renderer.Render( rendererParameters );
}
+
+Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout )
+{
+ if( textParameters.text.empty() )
+ {
+ Dali::Devel::PixelBuffer pixelBuffer = Dali::Devel::PixelBuffer::New( textParameters.textWidth,
+ textParameters.textHeight,
+ Dali::Pixel::RGBA8888 );
+
+ const unsigned int bufferSize = textParameters.textWidth * textParameters.textHeight * Dali::Pixel::GetBytesPerPixel(Dali::Pixel::RGBA8888);
+ unsigned char* buffer = pixelBuffer.GetBuffer();
+ memset(buffer, 0, bufferSize);
+
+ return pixelBuffer;
+ }
+
+ FontClient fontClient = FontClient::Get();
+ MetricsPtr metrics;
+ // Use this to access FontClient i.e. to get down-scaled Emoji metrics.
+ metrics = Metrics::New( fontClient );
+
+ Text::ModelPtr textModel = Text::Model::New();
+ InternalDataModel internalData( fontClient, metrics, textModel );
+
+ TextAbstraction::TextRenderer::Parameters rendererParameters( internalData.textModel->mVisualModel->mGlyphs,
+ internalData.textModel->mVisualModel->mGlyphPositions,
+ internalData.textModel->mVisualModel->mColors,
+ internalData.textModel->mVisualModel->mColorIndices,
+ internalData.blendingMode,
+ internalData.isEmoji );
+
+
+ rendererParameters.width = textParameters.textWidth;
+ rendererParameters.height = textParameters.textHeight;
+ rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888; // @note: At the moment all textures are generated RGBA8888
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Process the markup string if the mark-up processor is enabled.
+ ////////////////////////////////////////////////////////////////////////////////
+ ShapeTextPreprocess( textParameters, rendererParameters, internalData );
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Retrieve the glyphs. Text shaping
+ ////////////////////////////////////////////////////////////////////////////////
+ ShapeText( rendererParameters, embeddedItemLayout, internalData );
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Retrieve the glyph's metrics.
+ ////////////////////////////////////////////////////////////////////////////////
+
+ metrics->GetGlyphMetrics( rendererParameters.glyphs.Begin(), internalData.numberOfGlyphs );
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Set the color runs in glyphs.
+ ////////////////////////////////////////////////////////////////////////////////
+ SetColorSegmentation( textParameters, internalData );
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Set the isEmoji Vector
+ ////////////////////////////////////////////////////////////////////////////////
+ SetEmojiVector( internalData );
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Layout the text
+ ////////////////////////////////////////////////////////////////////////////////
+ Size newLayoutSize = LayoutText( textParameters, rendererParameters, embeddedItemLayout, internalData );
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Align the text.
+ ////////////////////////////////////////////////////////////////////////////////
+ Align( textParameters, rendererParameters, embeddedItemLayout, internalData, newLayoutSize );
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Ellipsis the text.
+ ////////////////////////////////////////////////////////////////////////////////
+ Ellipsis( textParameters, rendererParameters, embeddedItemLayout, internalData );
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Render the text.
+ ////////////////////////////////////////////////////////////////////////////////
+ return RenderText( textParameters, rendererParameters );
+}
+
+
Devel::PixelBuffer CreateShadow( const ShadowParameters& shadowParameters )
{
// The size of the pixel data.
}
}
+
+Dali::Property::Array RenderForLastIndex( RendererParameters& textParameters )
+{
+ Property::Array offsetValues;
+ if( textParameters.text.empty() )
+ {
+ return offsetValues;
+ }
+ FontClient fontClient = FontClient::Get();
+ MetricsPtr metrics;
+ metrics = Metrics::New( fontClient );
+
+ Text::ModelPtr textModel = Text::Model::New();
+ InternalDataModel internalData( fontClient, metrics, textModel );
+
+ TextAbstraction::TextRenderer::Parameters rendererParameters( textModel->mVisualModel->mGlyphs,
+ textModel->mVisualModel->mGlyphPositions,
+ textModel->mVisualModel->mColors,
+ textModel->mVisualModel->mColorIndices,
+ internalData.blendingMode,
+ internalData.isEmoji );
+
+
+ rendererParameters.width = textParameters.textWidth;
+ rendererParameters.height = textParameters.textHeight;
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Process the markup string if the mark-up processor is enabled.
+ ////////////////////////////////////////////////////////////////////////////////
+ ShapeTextPreprocess( textParameters, rendererParameters, internalData );
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Retrieve the glyphs. Text shaping
+ ////////////////////////////////////////////////////////////////////////////////
+ Dali::Vector<Dali::Toolkit::DevelText::EmbeddedItemInfo> embeddedItemLayout;
+ ShapeText( rendererParameters, embeddedItemLayout, internalData );
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Retrieve the glyph's metrics.
+ ////////////////////////////////////////////////////////////////////////////////
+ metrics->GetGlyphMetrics( rendererParameters.glyphs.Begin(), internalData.numberOfGlyphs );
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Layout the text
+ ////////////////////////////////////////////////////////////////////////////////
+ int boundingBox = textParameters.textHeight - (textParameters.padding.top + textParameters.padding.bottom);
+ textParameters.textHeight = MAX_INT; // layout for the entire area.
+ LayoutText( textParameters, rendererParameters, embeddedItemLayout, internalData );
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Calculation last character index
+ ////////////////////////////////////////////////////////////////////////////////
+ Vector<LineRun>& lines = internalData.textModel->mVisualModel->mLines;
+ unsigned int numberOfLines = lines.Count();
+ int numberOfCharacters = 0;
+ float penY = 0.f;
+ float lineSize = internalData.layoutEngine.GetDefaultLineSize();
+ float lineOffset = 0.f;
+ for( unsigned int index = 0u; index < numberOfLines; ++index )
+ {
+ const LineRun& line = *( lines.Begin() + index );
+ numberOfCharacters += line.characterRun.numberOfCharacters;
+
+ lineOffset = lineSize > 0.f ? lineSize : ( line.ascender + -line.descender );
+ penY += lineOffset;
+ if( ( penY + lineOffset ) > boundingBox )
+ {
+ offsetValues.PushBack( numberOfCharacters );
+ penY = 0.f;
+ }
+ }
+ if( penY > 0.f)
+ {
+ // add remain character index
+ offsetValues.PushBack( numberOfCharacters );
+ }
+
+ return offsetValues;
+}
+
+
+Dali::Property::Array GetLastCharacterIndex( RendererParameters& textParameters )
+{
+ Dali::Property::Array offsetValues = Toolkit::DevelText::RenderForLastIndex( textParameters );
+ return offsetValues;
+}
+
+
} // namespace DevelText
} // namespace Toolkit
*/
// EXTERNAL INCLUDES
+#include <dali/public-api/object/property-array.h>
#include <dali-toolkit/public-api/dali-toolkit-common.h>
#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
fontWeight{},
fontWidth{},
fontSlant{},
- layout{ "singleLine" },
- circularAlignment{ "begin" },
- textColor{ Color::WHITE },
- fontSize{ 0.f },
- textWidth{ 0u },
- textHeight{ 0u },
- radius{ 0u },
- beginAngle{ 0.f },
- incrementAngle{ 0.f },
- ellipsisEnabled{ true },
- markupEnabled{ false },
- isTextColorSet{ false }
- {}
+ layout{"singleLine"},
+ circularAlignment{"begin"},
+ textColor{Color::WHITE},
+ fontSize{0.f},
+ textWidth{0u},
+ textHeight{0u},
+ radius{0u},
+ beginAngle{0.f},
+ incrementAngle{0.f},
+ ellipsisEnabled{true},
+ markupEnabled{false},
+ isTextColorSet{false},
+ minLineSize{0.f},
+ padding{0u, 0u, 0u, 0u}
+ {
+ }
std::string text; ///< The text to be rendered encoded in utf8.
bool ellipsisEnabled:1; ///< Whether the ellipsis layout option is enabled.
bool markupEnabled:1; ///< Whether the mark-up processor is enabled.
bool isTextColorSet:1; ///< Whether a default color has been set.
+
+ float minLineSize; ///< The line's minimum size (in points).
+
+ Extents padding; ///< The padding of the boundaries where the text is going to be laid-out.
};
/**
*/
DALI_TOOLKIT_API void UpdateBuffer( Devel::PixelBuffer src, Devel::PixelBuffer dst, unsigned int x, unsigned int y, bool blend);
+/**
+ * @brief Splits the text in pages of the size given in @p textParameters
+ *
+ * @note The returned indices are indices to utf32 characters. The input text is encoded in utf8.
+ * @return An array with the indices of the last character of each page
+ */
+DALI_TOOLKIT_API Dali::Property::Array GetLastCharacterIndex( RendererParameters& textParameters );
+
} // namespace DevelText
} // namespace Toolkit
DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "textBackground", MAP, BACKGROUND )
DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "ignoreSpacesAfterText", BOOLEAN, IGNORE_SPACES_AFTER_TEXT )
DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "matchSystemLanguageDirection", BOOLEAN, MATCH_SYSTEM_LANGUAGE_DIRECTION )
-DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "textFit", MAP, TEXT_FIT )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "textFit", MAP, TEXT_FIT )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "minLineSize", FLOAT, MIN_LINE_SIZE )
DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT( Toolkit, TextLabel, "textColor", Color::BLACK, TEXT_COLOR )
DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, TextLabel, "textColorRed", TEXT_COLOR_RED, TEXT_COLOR, 0 )
DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, TextLabel, "textColorGreen", TEXT_COLOR_GREEN, TEXT_COLOR, 1 )
}
break;
}
+ case Toolkit::DevelTextLabel::Property::MIN_LINE_SIZE:
+ {
+ if( impl.mController )
+ {
+ const float lineSize = value.Get<float>();
+
+ if( impl.mController->SetDefaultLineSize( lineSize ) )
+ {
+ impl.mTextUpdateNeeded = true;
+ }
+ }
+ break;
+ }
}
// Request relayout when text update is needed. It's necessary to call it
value = map;
break;
}
+ case Toolkit::DevelTextLabel::Property::MIN_LINE_SIZE:
+ {
+ if( impl.mController )
+ {
+ value = impl.mController->GetDefaultLineSize();
+ }
+ break;
+ }
}
}
const float MAX_FLOAT = std::numeric_limits<float>::max();
const CharacterDirection LTR = false;
const CharacterDirection RTL = !LTR;
-const float LINE_SPACING= 0.f;
+const float LINE_SPACING = 0.f;
+const float MIN_LINE_SIZE = 0.f;
inline bool isEmptyLineAtLast( const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line )
{
Impl()
: mLayout{ Layout::Engine::SINGLE_LINE_BOX },
mCursorWidth{ 0.f },
- mDefaultLineSpacing{ LINE_SPACING }
+ mDefaultLineSpacing{ LINE_SPACING },
+ mDefaultLineSize{ MIN_LINE_SIZE }
{
}
// Sets the minimum descender.
lineLayout.descender = std::min( lineLayout.descender, fontMetrics.descender );
- // set the line spacing
- lineLayout.lineSpacing = mDefaultLineSpacing;
+ // Sets the line size
+ lineLayout.lineSpacing = mDefaultLineSize - ( lineLayout.ascender + -lineLayout.descender );
+ lineLayout.lineSpacing = lineLayout.lineSpacing < 0.f ? 0.f : lineLayout.lineSpacing;
+
+ // Add the line spacing
+ lineLayout.lineSpacing += mDefaultLineSpacing;
}
/**
lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
lineRun.characterRun.characterIndex = layout.characterIndex;
lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
- lineRun.lineSpacing = mDefaultLineSpacing;
-
lineRun.width = layout.length;
lineRun.extraLength = std::ceil( layout.whiteSpaceLengthEndOfLine );
lineRun.direction = layout.direction;
lineRun.ellipsis = false;
+ lineRun.lineSpacing = mDefaultLineSize - ( lineRun.ascender + -lineRun.descender );
+ lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
+
+ lineRun.lineSpacing += mDefaultLineSpacing;
+
+
// Update the actual size.
if( lineRun.width > layoutSize.width )
{
lineRun.alignmentOffset = 0.f;
lineRun.direction = LTR;
lineRun.ellipsis = false;
- lineRun.lineSpacing = mDefaultLineSpacing;
+
+ lineRun.lineSpacing = mDefaultLineSize - ( lineRun.ascender + -lineRun.descender );
+ lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
+
+ lineRun.lineSpacing += mDefaultLineSpacing;
layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
}
// Updates the vertical pen's position.
penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
+ // If there is a defaultLineSize, updates the pen's position.
+ if( mDefaultLineSize > 0.f )
+ {
+ float lineSpacing = mDefaultLineSize - ( layout.ascender + -layout.descender );
+ lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
+ penY += lineSpacing;
+ }
// Increase the glyph index.
index = nextIndex;
Type mLayout;
float mCursorWidth;
float mDefaultLineSpacing;
+ float mDefaultLineSize;
IntrusivePtr<Metrics> mMetrics;
};
return mImpl->mDefaultLineSpacing;
}
+void Engine::SetDefaultLineSize( float lineSize )
+{
+ mImpl->mDefaultLineSize = lineSize;
+}
+
+float Engine::GetDefaultLineSize() const
+{
+ return mImpl->mDefaultLineSize;
+}
+
} // namespace Layout
} // namespace Text
*/
float GetDefaultLineSpacing() const;
+ /**
+ * @brief Sets the default line size.
+ *
+ * @param[in] lineSize The line size.
+ */
+ void SetDefaultLineSize( float lineSize );
+
+ /**
+ * @brief Retrieves the default line size.
+ *
+ * @return The line size.
+ */
+ float GetDefaultLineSize() const;
+
private:
// Undefined
case VerticalAlignment::CENTER:
{
penY = static_cast<int>( 0.5f * ( size.height - layoutSize.height ) );
+ penY = penY < 0.f ? 0.f : penY;
break;
}
case VerticalAlignment::BOTTOM:
const Length currentNumberOfGlyphs = glyphs.Count();
const Length numberOfGlyphsReserved = static_cast<Length>( numberOfCharacters * 1.3f );
- glyphs.Resize( currentNumberOfGlyphs + numberOfGlyphsReserved, glyphInfo );
- glyphToCharacterMap.Resize( currentNumberOfGlyphs + numberOfGlyphsReserved );
+ glyphs.Reserve( currentNumberOfGlyphs + numberOfGlyphsReserved );
+ glyphToCharacterMap.Reserve( currentNumberOfGlyphs + numberOfGlyphsReserved );
// The actual number of glyphs.
Length totalNumberOfGlyphs = currentNumberOfGlyphs;
const char * const PLACEHOLDER_POINT_SIZE = "pointSize";
const char * const PLACEHOLDER_PIXEL_SIZE = "pixelSize";
const char * const PLACEHOLDER_ELLIPSIS = "ellipsis";
-const unsigned int MAX_TEXT_LENGTH = 1024u * 32u;
float ConvertToEven( float value )
{
void Controller::SetMaximumNumberOfCharacters( Length maxCharacters )
{
- mImpl->mMaximumNumberOfCharacters = std::min( maxCharacters, MAX_TEXT_LENGTH );
+ mImpl->mMaximumNumberOfCharacters = maxCharacters;
}
int Controller::GetMaximumNumberOfCharacters()
utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
}
- // Limit the text size. If the text size is too large, crash or deadlock will occur.
- if( textSize > MAX_TEXT_LENGTH )
- {
- DALI_LOG_WARNING( "The text size is too large(%d), limit the length to 32,768u\n", textSize );
- textSize = MAX_TEXT_LENGTH;
- }
-
// Convert text into UTF-32
Vector<Character>& utf32Characters = mImpl->mModel->mLogicalModel->mText;
utf32Characters.Resize( textSize );
return mImpl->mLayoutEngine.GetDefaultLineSpacing();
}
+bool Controller::SetDefaultLineSize( float lineSize )
+{
+ if( std::fabs( lineSize - mImpl->mLayoutEngine.GetDefaultLineSize() ) > Math::MACHINE_EPSILON_1000 )
+ {
+ mImpl->mLayoutEngine.SetDefaultLineSize(lineSize);
+ mImpl->mRecalculateNaturalSize = true;
+ return true;
+ }
+ return false;
+}
+
+float Controller::GetDefaultLineSize() const
+{
+ return mImpl->mLayoutEngine.GetDefaultLineSize();
+}
+
void Controller::SetInputColor( const Vector4& color )
{
if( NULL != mImpl->mEventData )
float GetDefaultLineSpacing() const;
/**
+ * @brief Sets the default line size.
+ *
+ * @param[in] lineSize The line size.
+ *
+ * @return True if lineSize has been updated, false otherwise
+ */
+ bool SetDefaultLineSize( float lineSize );
+
+ /**
+ * @brief Retrieves the default line size.
+ *
+ * @return The line size.
+ */
+ float GetDefaultLineSize() const;
+
+ /**
* @brief Sets the input text's color.
*
* @param[in] color The input text's color.
#include <dali/public-api/animation/constraints.h>
#include <dali/devel-api/rendering/renderer-devel.h>
#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
+#include <dali/devel-api/images/pixel-data-devel.h>
+#include <string.h>
// INTERNAL HEADER
#include <dali-toolkit/public-api/visuals/text-visual-properties.h>
// Renderer needs textures and to be added to control
mRendererUpdateNeeded = true;
+ mRendererList.push_back( mImpl->mRenderer );
+
UpdateRenderer();
}
-void TextVisual::DoSetOffStage( Actor& actor )
+void TextVisual::RemoveRenderer( Actor& actor )
{
- if( mImpl->mRenderer )
+ for( RendererContainer::iterator iter = mRendererList.begin(); iter != mRendererList.end(); ++iter)
{
- // Removes the renderer from the actor.
- actor.RemoveRenderer( mImpl->mRenderer );
+ Renderer renderer = (*iter);
+ if( renderer )
+ {
+ // Removes the renderer from the actor.
+ actor.RemoveRenderer( renderer );
+ }
+ }
+ // Clear the renderer list
+ mRendererList.clear();
+}
- RemoveTextureSet();
+void TextVisual::DoSetOffStage( Actor& actor )
+{
+ RemoveRenderer( actor );
- // Resets the renderer.
- mImpl->mRenderer.Reset();
- }
+ // Resets the renderer.
+ mImpl->mRenderer.Reset();
// Resets the control handle.
mControl.Reset();
if( ( fabsf( relayoutSize.width ) < Math::MACHINE_EPSILON_1000 ) || ( fabsf( relayoutSize.height ) < Math::MACHINE_EPSILON_1000 ) || text.empty() )
{
- // Removes the texture set.
- RemoveTextureSet();
-
- // Remove any renderer previously set.
- if( mImpl->mRenderer )
- {
- control.RemoveRenderer( mImpl->mRenderer );
- }
+ // Remove the texture set and any renderer previously set.
+ RemoveRenderer( control );
// Nothing else to do if the relayout size is zero.
ResourceReady( Toolkit::Visual::ResourceStatus::READY );
{
mRendererUpdateNeeded = false;
- // Removes the texture set.
- RemoveTextureSet();
-
- // Remove any renderer previously set.
- if( mImpl->mRenderer )
- {
- control.RemoveRenderer( mImpl->mRenderer );
- }
+ // Remove the texture set and any renderer previously set.
+ RemoveRenderer( control );
if( ( relayoutSize.width > Math::MACHINE_EPSILON_1000 ) &&
( relayoutSize.height > Math::MACHINE_EPSILON_1000 ) )
const bool styleEnabled = ( shadowEnabled || underlineEnabled || outlineEnabled || backgroundEnabled );
- TextureSet textureSet = GetTextTexture( relayoutSize, hasMultipleTextColors, containsColorGlyph, styleEnabled );
- mImpl->mRenderer.SetTextures( textureSet );
- Shader shader = GetTextShader( mFactoryCache, hasMultipleTextColors, containsColorGlyph, styleEnabled );
- mImpl->mRenderer.SetShader(shader);
+ AddRenderer( control, relayoutSize, hasMultipleTextColors, containsColorGlyph, styleEnabled );
- mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
+ // Text rendered and ready to display
+ ResourceReady( Toolkit::Visual::ResourceStatus::READY );
+ }
+ }
+}
- mImpl->mRenderer.RegisterProperty( "uHasMultipleTextColors", static_cast<float>( hasMultipleTextColors ) );
+void TextVisual::AddTexture( TextureSet& textureSet, PixelData& data, Sampler& sampler, unsigned int textureSetIndex )
+{
+ Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D,
+ data.GetPixelFormat(),
+ data.GetWidth(),
+ data.GetHeight() );
+ texture.Upload( data );
+
+ textureSet.SetTexture( textureSetIndex, texture );
+ textureSet.SetSampler( textureSetIndex, sampler );
+}
- mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON);
+PixelData TextVisual::ConvertToPixelData( unsigned char* buffer, int width, int height, int offsetPosition, const Pixel::Format textPixelFormat )
+{
+ int bpp = Pixel::GetBytesPerPixel( textPixelFormat );
+ unsigned int bufferSize = width * height * bpp;
+ unsigned char* dstBuffer = static_cast<unsigned char*>( malloc ( bufferSize ) );
+ memcpy( dstBuffer, buffer + offsetPosition * bpp, bufferSize );
+ PixelData pixelData = Dali::PixelData::New( dstBuffer,
+ bufferSize,
+ width,
+ height,
+ textPixelFormat,
+ Dali::PixelData::FREE );
+ return pixelData;
+}
- //Register transform properties
- mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
+void TextVisual::CreateTextureSet( TilingInfo& info, Renderer& renderer, Sampler& sampler, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled )
+{
- control.AddRenderer( mImpl->mRenderer );
+ TextureSet textureSet = TextureSet::New();
+ unsigned int textureSetIndex = 0u;
- // Text rendered and ready to display
- ResourceReady( Toolkit::Visual::ResourceStatus::READY );
- }
+ // Convert the buffer to pixel data to make it a texture.
+ if( info.textBuffer )
+ {
+ PixelData data = ConvertToPixelData( info.textBuffer, info.width, info.height, info.offsetPosition, info.textPixelFormat );
+ AddTexture( textureSet, data, sampler, textureSetIndex );
+ ++textureSetIndex;
}
+
+ if( styleEnabled && info.styleBuffer )
+ {
+ PixelData styleData = ConvertToPixelData( info.styleBuffer, info.width, info.height, info.offsetPosition, Pixel::RGBA8888 );
+ AddTexture( textureSet, styleData, sampler, textureSetIndex );
+ ++textureSetIndex;
+ }
+
+ if( containsColorGlyph && !hasMultipleTextColors && info.maskBuffer )
+ {
+ PixelData maskData = ConvertToPixelData( info.maskBuffer, info.width, info.height, info.offsetPosition, Pixel::L8 );
+ AddTexture( textureSet, maskData, sampler, textureSetIndex );
+ }
+
+ renderer.SetTextures( textureSet );
+
+ //Register transform properties
+ mImpl->mTransform.RegisterUniforms( renderer, Direction::LEFT_TO_RIGHT );
+
+ // Enable the pre-multiplied alpha to improve the text quality
+ renderer.SetProperty( Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true );
+ renderer.RegisterProperty( PREMULTIPLIED_ALPHA, 1.0f );
+
+ // Set size and offset for the tiling.
+ renderer.RegisterProperty( SIZE, Vector2( info.width, info.height ) );
+ renderer.RegisterProperty( OFFSET, Vector2( info.offSet.x, info.offSet.y ) );
+ renderer.RegisterProperty( "uHasMultipleTextColors", static_cast<float>( hasMultipleTextColors ) );
+ renderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON);
+
+ mRendererList.push_back( renderer );
}
-void TextVisual::RemoveTextureSet()
+
+void TextVisual::AddRenderer( Actor& actor, const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled )
{
- if( mImpl->mFlags & Impl::IS_ATLASING_APPLIED )
+ Shader shader = GetTextShader( mFactoryCache, hasMultipleTextColors, containsColorGlyph, styleEnabled );
+ mImpl->mRenderer.SetShader( shader );
+
+ // Get the maximum size.
+ const int maxTextureSize = Dali::GetMaxTextureSize();
+
+ // No tiling required. Use the default renderer.
+ if( size.height < maxTextureSize )
+ {
+ TextureSet textureSet = GetTextTexture( size, hasMultipleTextColors, containsColorGlyph, styleEnabled );
+
+ mImpl->mRenderer.SetTextures( textureSet );
+ //Register transform properties
+ mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
+ mImpl->mRenderer.RegisterProperty( "uHasMultipleTextColors", static_cast<float>( hasMultipleTextColors ) );
+ mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON);
+
+ mRendererList.push_back( mImpl->mRenderer );
+ }
+ // If the pixel data exceeds the maximum size, tiling is required.
+ else
{
- // Removes the text's image from the texture atlas.
- Vector4 atlasRect;
+ // Filter mode needs to be set to linear to produce better quality while scaling.
+ Sampler sampler = Sampler::New();
+ sampler.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR );
+
+ // Create RGBA texture if the text contains emojis or multiple text colors, otherwise L8 texture
+ Pixel::Format textPixelFormat = ( containsColorGlyph || hasMultipleTextColors ) ? Pixel::RGBA8888 : Pixel::L8;
+
+ // Check the text direction
+ Toolkit::DevelText::TextDirection::Type textDirection = mController->GetTextDirection();
+
+ // Create a texture for the text without any styles
+ PixelData data = mTypesetter->Render( size, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat );
+
+ int verifiedWidth = data.GetWidth();
+ int verifiedHeight = data.GetHeight();
+
+ // Set information for creating textures.
+ TilingInfo info( verifiedWidth, maxTextureSize, textPixelFormat );
+
+ // Get the buffer of text.
+ Dali::DevelPixelData::PixelDataBuffer textPixelData = Dali::DevelPixelData::ReleasePixelDataBuffer( data );
+ info.textBuffer = textPixelData.buffer;
+
+ if( styleEnabled )
+ {
+ // Create RGBA texture for all the text styles (without the text itself)
+ PixelData styleData = mTypesetter->Render( size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888 );
+ Dali::DevelPixelData::PixelDataBuffer stylePixelData = Dali::DevelPixelData::ReleasePixelDataBuffer( styleData );
+ info.styleBuffer = stylePixelData.buffer;
+ }
+
+ if ( containsColorGlyph && !hasMultipleTextColors )
+ {
+ // Create a L8 texture as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
+ PixelData maskData = mTypesetter->Render( size, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8 );
+ Dali::DevelPixelData::PixelDataBuffer maskPixelData = Dali::DevelPixelData::ReleasePixelDataBuffer( maskData );
+ info.maskBuffer = maskPixelData.buffer;
+ }
+
+ // Get the current offset for recalculate the offset when tiling.
+ Property::Map retMap;
+ mImpl->mTransform.GetPropertyMap( retMap );
+ Vector2 offSet = retMap.Find( Dali::Toolkit::Visual::Transform::Property::OFFSET )->Get< Vector2 >();
+ info.offSet = offSet;
+
+ // Create a textureset in the default renderer.
+ CreateTextureSet( info, mImpl->mRenderer, sampler, hasMultipleTextColors, containsColorGlyph, styleEnabled );
- const Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
- if( index != Property::INVALID_INDEX )
+ verifiedHeight -= maxTextureSize;
+
+ Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
+
+ int offsetPosition = verifiedWidth * maxTextureSize;
+ // Create a renderer by cutting maxTextureSize.
+ while( verifiedHeight > 0 )
{
- const Property::Value& atlasRectValue = mImpl->mRenderer.GetProperty( index );
- atlasRectValue.Get( atlasRect );
+ Renderer tilingRenderer = Renderer::New( geometry, shader );
+ tilingRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT );
+ // New offset position of buffer for tiling.
+ info.offsetPosition += offsetPosition;
+ // New height for tiling.
+ info.height = ( verifiedHeight - maxTextureSize ) > 0 ? maxTextureSize : verifiedHeight;
+ // New offset for tiling.
+ info.offSet.y += maxTextureSize;
+ // Create a textureset int the new tiling renderer.
+ CreateTextureSet( info, tilingRenderer, sampler, hasMultipleTextColors, containsColorGlyph, styleEnabled );
+
+ verifiedHeight -= maxTextureSize;
+ }
+ }
+
+ mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
- const TextureSet& textureSet = mImpl->mRenderer.GetTextures();
- mFactoryCache.GetAtlasManager()->Remove( textureSet, atlasRect );
+ for( RendererContainer::iterator iter = mRendererList.begin(); iter != mRendererList.end(); ++iter)
+ {
+ Renderer renderer = (*iter);
+ if( renderer )
+ {
+ actor.AddRenderer( renderer );
}
}
}
+
TextureSet TextVisual::GetTextTexture( const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled )
{
// Filter mode needs to be set to linear to produce better quality while scaling.
// It may happen the image atlas can't handle a pixel data it exceeds the maximum size.
// In that case, create a texture. TODO: should tile the text.
+ unsigned int textureSetIndex = 0u;
- Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D,
- data.GetPixelFormat(),
- data.GetWidth(),
- data.GetHeight() );
-
- texture.Upload( data );
-
- textureSet.SetTexture( 0u, texture );
- textureSet.SetSampler( 0u, sampler );
+ AddTexture( textureSet, data, sampler, textureSetIndex );
+ ++textureSetIndex;
if ( styleEnabled )
{
// Create RGBA texture for all the text styles (without the text itself)
PixelData styleData = mTypesetter->Render( size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888 );
- Texture styleTexture = Texture::New( Dali::TextureType::TEXTURE_2D,
- styleData.GetPixelFormat(),
- styleData.GetWidth(),
- styleData.GetHeight() );
-
- styleTexture.Upload( styleData );
-
- textureSet.SetTexture( 1u, styleTexture );
- textureSet.SetSampler( 1u, sampler );
+ AddTexture( textureSet, styleData, sampler, textureSetIndex );
+ ++textureSetIndex;
}
if ( containsColorGlyph && !hasMultipleTextColors )
// Create a L8 texture as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
PixelData maskData = mTypesetter->Render( size, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8 );
- Texture maskTexture = Texture::New( Dali::TextureType::TEXTURE_2D,
- maskData.GetPixelFormat(),
- maskData.GetWidth(),
- maskData.GetHeight() );
-
- maskTexture.Upload( maskData );
-
- if ( !styleEnabled )
- {
- textureSet.SetTexture( 1u, maskTexture );
- textureSet.SetSampler( 1u, sampler );
- }
- else
- {
- textureSet.SetTexture( 2u, maskTexture );
- textureSet.SetSampler( 2u, sampler );
- }
+ AddTexture( textureSet, maskData, sampler, textureSetIndex );
}
return textureSet;
void OnSetTransform() override;
private:
+
+ struct TilingInfo
+ {
+ unsigned char* textBuffer;
+ unsigned char* styleBuffer;
+ unsigned char* maskBuffer;
+ int width;
+ int height;
+ Pixel::Format textPixelFormat;
+ int offsetPosition;
+ Vector2 offSet;
+
+ TilingInfo( int width, int height, Pixel::Format textPixelFormat )
+ : textBuffer( NULL ),
+ styleBuffer( NULL ),
+ maskBuffer( NULL ),
+ width( width ),
+ height( height ),
+ textPixelFormat( textPixelFormat ),
+ offsetPosition( 0 ),
+ offSet( 0.f, 0.f )
+ {
+ }
+
+ ~TilingInfo()
+ {
+ if( textBuffer )
+ {
+ free( textBuffer );
+ }
+ if( styleBuffer )
+ {
+ free( styleBuffer );
+ }
+ if( maskBuffer )
+ {
+ free( maskBuffer );
+ }
+ }
+
+ };
+
/**
* @brief Set the individual property to the given value.
*
void UpdateRenderer();
/**
- * @brief Removes the texture set from the renderer.
+ * @brief Removes the text's renderer.
+ */
+ void RemoveRenderer( Actor& actor );
+
+ /**
+ * @brief Create a texture in textureSet and add it.
+ * @param[in] textureSet The textureSet to which the texture will be added.
+ * @param[in] data The PixelData to be uploaded to texture
+ * @param[in] sampler The sampler.
+ * @param[in] textureSetIndex The Index of TextureSet.
+ */
+ void AddTexture( TextureSet& textureSet, PixelData& data, Sampler& sampler, unsigned int textureSetIndex );
+
+ /**
+ * @brief Convert the buffer to pixelData.
+ * @param[in] buffer The Buffer to be converted to pixelData.
+ * @param[in] width The width of pixel data.
+ * @param[in] height The height of pixel data.
+ * @param[in] offsetPosition The The buffer's start position.
+ * @param[in] textPixelFormat The PixelForma of text.
+ */
+ PixelData ConvertToPixelData( unsigned char* buffer, int width, int height, int offsetPosition, const Pixel::Format textPixelFormat );
+
+ /**
+ * @brief Create the text's texture.
+ * @param[in] info This is the information you need to create a Tiling.
+ * @param[in] renderer The renderer to which the TextureSet will be added.
+ * @param[in] sampler The sampler.
+ * @param[in] hasMultipleTextColors Whether the text contains multiple colors.
+ * @param[in] containsColorGlyph Whether the text contains color glyph.
+ * @param[in] styleEnabled Whether the text contains any styles (e.g. shadow, underline, etc.).
*/
- void RemoveTextureSet();
+ void CreateTextureSet( TilingInfo& info, Renderer& renderer, Sampler& sampler, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled );
+
+ /**
+ * Create renderer of the text for rendering.
+ * @param[in] actor The actor.
+ * @param[in] size The texture size.
+ * @param[in] hasMultipleTextColors Whether the text contains multiple colors.
+ * @param[in] containsColorGlyph Whether the text contains color glyph.
+ * @param[in] styleEnabled Whether the text contains any styles (e.g. shadow, underline, etc.).
+ */
+ void AddRenderer( Actor& actor, const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled );
+
/**
* Get the texture of the text for rendering.
private:
+ typedef std::vector< Renderer > RendererContainer;
+
/**
* Used as an alternative to boolean so that it is obvious whether the text contains single or multiple text colors, and emoji and styles.
*/
};
};
+
private:
Text::ControllerPtr mController; ///< The text's controller.
Text::TypesetterPtr mTypesetter; ///< The text's typesetter.
WeakHandle<Actor> mControl; ///< The control where the renderer is added.
Property::Index mAnimatableTextColorPropertyIndex; ///< The index of animatable text color property registered by the control.
bool mRendererUpdateNeeded:1; ///< The flag to indicate whether the renderer needs to be updated.
+ RendererContainer mRendererList;
};
} // namespace Internal