label.SetProperty( DevelTextLabel::Property::TEXT_COLOR_ALPHA, 0.6f );
DALI_TEST_EQUALS( label.GetProperty< float >( DevelTextLabel::Property::TEXT_COLOR_ALPHA ), 0.6f, TEST_LOCATION );
DALI_TEST_EQUALS( label.GetProperty< Vector4 >( DevelTextLabel::Property::TEXT_COLOR_ANIMATABLE ), Vector4( 0.0f, 0.0f, 1.0f, 0.6f ), TEST_LOCATION );
+ DALI_TEST_EQUALS( label.GetProperty< Vector4 >( TextLabel::Property::TEXT_COLOR ), Vector4( 0.0f, 0.0f, 1.0f, 0.6f ), TEST_LOCATION );
END_TEST;
}
* @details name "enableMarkup", type BOOLEAN
*/
ENABLE_MARKUP,
+
+ /**
+ * @brief The shadow parameters.
+ * @details name "shadow", type MAP.
+ */
+ SHADOW,
+
+ /**
+ * @brief The default underline parameters.
+ * @details name "underline", type MAP.
+ */
+ UNDERLINE,
};
} // namespace Property
#include <dali-toolkit/internal/text/text-definitions.h>
#include <dali-toolkit/internal/styling/style-manager-impl.h>
+#include <dali-toolkit/devel-api/align-enums.h>
+#include <dali-toolkit/devel-api/controls/control-devel.h>
+#include <dali-toolkit/devel-api/visual-factory/visual-base.h>
+#include <dali-toolkit/devel-api/visuals/text-visual-properties.h>
+#include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
+#include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
+
using namespace Dali::Toolkit::Text;
namespace Dali
// Setup properties, signals and actions using the type-registry.
DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TextLabel, Toolkit::Control, Create );
-DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "renderingBackend", INTEGER, RENDERING_BACKEND )
+DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "renderingBackend", INTEGER, RENDERING_BACKEND ) // Deprecated property
DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "text", STRING, TEXT )
DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "fontFamily", STRING, FONT_FAMILY )
DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "fontStyle", MAP, FONT_STYLE )
DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "autoScrollStopMode", STRING, AUTO_SCROLL_STOP_MODE )
DALI_DEVEL_PROPERTY_REGISTRATION_READ_ONLY( Toolkit, TextLabel, "lineCount", INTEGER, LINE_COUNT )
DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "lineWrapMode", STRING, LINE_WRAP_MODE )
-DALI_DEVEL_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT( Toolkit, TextLabel, "textColorAnimatable", Color::WHITE, TEXT_COLOR_ANIMATABLE )
+DALI_DEVEL_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT( Toolkit, TextLabel, "textColorAnimatable", Color::BLACK, TEXT_COLOR_ANIMATABLE )
DALI_DEVEL_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, TextLabel, "textColorRed", TEXT_COLOR_RED, TEXT_COLOR_ANIMATABLE, 0)
DALI_DEVEL_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, TextLabel, "textColorGreen", TEXT_COLOR_GREEN, TEXT_COLOR_ANIMATABLE, 1)
DALI_DEVEL_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, TextLabel, "textColorBlue", TEXT_COLOR_BLUE, TEXT_COLOR_ANIMATABLE, 2)
{
case Toolkit::TextLabel::Property::RENDERING_BACKEND:
{
+ DALI_LOG_WARNING("[%s] Using deprecated Property TextLabel::Property::RENDERING_BACKEND which is no longer supported and will be ignored\n", __FUNCTION__);
+
int backend = value.Get< int >();
#ifndef ENABLE_VECTOR_BASED_TEXT_RENDERING
if( impl.mRenderingBackend != backend )
{
impl.mRenderingBackend = backend;
- impl.mRenderer.Reset();
+ impl.mTextUpdateNeeded = true;
if( impl.mController )
{
case Toolkit::TextLabel::Property::TEXT_COLOR:
{
- SetProperty( object, DevelTextLabel::Property::TEXT_COLOR_ANIMATABLE, value );
+ label.SetProperty( DevelTextLabel::Property::TEXT_COLOR_ANIMATABLE, value );
+ impl.mTextUpdateNeeded = true;
break;
}
if ( impl.mController->GetShadowOffset() != shadowOffset )
{
impl.mController->SetShadowOffset( shadowOffset );
- impl.mRenderer.Reset();
+ impl.mTextUpdateNeeded = true;
}
}
break;
if ( impl.mController->GetShadowColor() != shadowColor )
{
impl.mController->SetShadowColor( shadowColor );
- impl.mRenderer.Reset();
+ impl.mTextUpdateNeeded = true;
}
}
break;
if ( impl.mController->GetUnderlineColor() != color )
{
impl.mController->SetUnderlineColor( color );
- impl.mRenderer.Reset();
+ impl.mTextUpdateNeeded = true;
}
}
break;
if ( impl.mController->IsUnderlineEnabled() != enabled )
{
impl.mController->SetUnderlineEnabled( enabled );
- impl.mRenderer.Reset();
+ impl.mTextUpdateNeeded = true;
}
}
break;
if( fabsf( impl.mController->GetUnderlineHeight() - height ) > Math::MACHINE_EPSILON_1000 )
{
impl.mController->SetUnderlineHeight( height );
- impl.mRenderer.Reset();
+ impl.mTextUpdateNeeded = true;
}
}
break;
{
const float lineSpacing = value.Get<float>();
impl.mController->SetDefaultLineSpacing( lineSpacing );
- impl.mRenderer.Reset();
+ impl.mTextUpdateNeeded = true;
}
break;
}
const bool update = SetUnderlineProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
if( update )
{
- impl.mRenderer.Reset();
+ impl.mTextUpdateNeeded = true;
}
break;
}
const bool update = SetShadowProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
if( update )
{
- impl.mRenderer.Reset();
+ impl.mTextUpdateNeeded = true;
}
break;
}
const bool update = SetEmbossProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
if( update )
{
- impl.mRenderer.Reset();
+ impl.mTextUpdateNeeded = true;
}
break;
}
const bool update = SetOutlineProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
if( update )
{
- impl.mRenderer.Reset();
+ impl.mTextUpdateNeeded = true;
}
break;
}
{
case Toolkit::TextLabel::Property::RENDERING_BACKEND:
{
+ DALI_LOG_WARNING("[%s] Using deprecated Property TextLabel::Property::RENDERING_BACKEND which is no longer supported and will be ignored\n", __FUNCTION__);
+
value = impl.mRenderingBackend;
break;
}
}
case Toolkit::TextLabel::Property::TEXT_COLOR:
{
- if( impl.mController )
- {
- value = impl.mController->GetDefaultColor();
- }
+ value = label.GetProperty( Toolkit::DevelTextLabel::Property::TEXT_COLOR_ANIMATABLE );
break;
}
case Toolkit::TextLabel::Property::SHADOW_OFFSET:
{
Actor self = Self();
- mController = Text::Controller::New( this );
+ Property::Map propertyMap;
+ propertyMap.Add( Toolkit::Visual::Property::TYPE, Toolkit::DevelVisual::TEXT );
- // When using the vector-based rendering, the size of the GLyphs are different
- TextAbstraction::GlyphType glyphType = (Text::RENDERING_VECTOR_BASED == mRenderingBackend) ? TextAbstraction::VECTOR_GLYPH : TextAbstraction::BITMAP_GLYPH;
- mController->SetGlyphType( glyphType );
+ mVisual = Toolkit::VisualFactory::Get().CreateVisual( propertyMap );
+ DevelControl::RegisterVisual( *this, Toolkit::TextLabel::Property::TEXT, mVisual );
+
+ TextVisual::SetAnimatableTextColorProperty( mVisual, Toolkit::DevelTextLabel::Property::TEXT_COLOR_ANIMATABLE );
+
+ mController = TextVisual::GetController(mVisual);
+ if( mController )
+ {
+ mController->SetControlInterface(this);
+ }
// Use height-for-width negotiation by default
self.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
Layout::Engine& engine = mController->GetLayoutEngine();
engine.SetCursorWidth( 0u ); // Do not layout space for the cursor.
-
- self.OnStageSignal().Connect( this, &TextLabel::OnStageConnect );
}
void TextLabel::OnStyleChange( Toolkit::StyleManager styleManager, StyleChange::Type change )
switch ( index )
{
- case Toolkit::TextLabel::Property::TEXT_COLOR:
case Toolkit::DevelTextLabel::Property::TEXT_COLOR_ANIMATABLE:
{
const Vector4& textColor = propertyValue.Get< Vector4 >();
if( mController->GetDefaultColor() != textColor )
{
mController->SetDefaultColor( textColor );
- mRenderer.Reset();
+ mTextUpdateNeeded = true;
}
break;
}
Self().GetPadding( padding );
Vector2 contentSize( size.x - ( padding.left + padding.right ), size.y - ( padding.top + padding.bottom ) );
-
const Text::Controller::UpdateTextType updateTextType = mController->Relayout( contentSize );
- if( ( Text::Controller::NONE_UPDATED != ( Text::Controller::MODEL_UPDATED & updateTextType ) ) ||
- !mRenderer )
+ if( ( Text::Controller::NONE_UPDATED != ( Text::Controller::MODEL_UPDATED & updateTextType ) )
+ || mTextUpdateNeeded )
{
- if( !mRenderer )
- {
- mRenderer = Text::Backend::Get().NewRenderer( mRenderingBackend );
- }
- RenderText();
- }
-}
-
-void TextLabel::RequestTextRelayout()
-{
- RelayoutRequest();
-}
+ DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::OnRelayout IsAutoScrollEnabled[%s] [%p]\n", ( mController->IsAutoScrollEnabled())?"true":"false", this );
-void TextLabel::RenderText()
-{
- DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::RenderText IsAutoScrollEnabled[%s] [%p]\n", ( mController->IsAutoScrollEnabled())?"true":"false", this );
+ // Update the visual
+ TextVisual::EnableRendererUpdate( mVisual );
- Actor self = Self();
- Actor renderableActor;
+ Padding padding;
+ Self().GetPadding( padding );
- float alignmentOffset = 0.f;
- if( mRenderer )
- {
-
- Dali::Toolkit::TextLabel handle = Dali::Toolkit::TextLabel( GetOwner() );
-
- renderableActor = mRenderer->Render( mController->GetView(),
- handle,
- Toolkit::DevelTextLabel::Property::TEXT_COLOR_ANIMATABLE,
- alignmentOffset,
- DepthIndex::CONTENT );
- }
-
- if( renderableActor != mRenderableActor )
- {
- UnparentAndReset( mRenderableActor );
-
- if( renderableActor )
- {
- const Vector2& scrollOffset = mController->GetTextModel()->GetScrollPosition();
- Padding padding;
- self.GetPadding( padding );
- renderableActor.SetPosition( scrollOffset.x + alignmentOffset + padding.left, scrollOffset.y + padding.top );
-
- self.Add( renderableActor );
- }
- mRenderableActor = renderableActor;
+ Property::Map visualTransform;
+ visualTransform.Add( Toolkit::DevelVisual::Transform::Property::SIZE, contentSize )
+ .Add( Toolkit::DevelVisual::Transform::Property::SIZE_POLICY, Vector2( DevelVisual::Transform::Policy::ABSOLUTE, DevelVisual::Transform::Policy::ABSOLUTE ) )
+ .Add( Toolkit::DevelVisual::Transform::Property::OFFSET, Vector2(padding.left, padding.top) )
+ .Add( Toolkit::DevelVisual::Transform::Property::OFFSET_POLICY, Vector2( Toolkit::DevelVisual::Transform::Policy::ABSOLUTE, Toolkit::DevelVisual::Transform::Policy::ABSOLUTE ) )
+ .Add( Toolkit::DevelVisual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN )
+ .Add( Toolkit::DevelVisual::Transform::Property::ANCHOR_POINT, Toolkit::Align::TOP_BEGIN );
+ mVisual.SetTransformAndSize( visualTransform, size );
if ( mController->IsAutoScrollEnabled() )
{
SetUpAutoScrolling();
}
+
+ mTextUpdateNeeded = false;
}
}
+void TextLabel::RequestTextRelayout()
+{
+ RelayoutRequest();
+}
+
void TextLabel::SetUpAutoScrolling()
{
const Size& controlSize = mController->GetView().GetControlSize();
- const Size offScreenSize = GetNaturalSize().GetVectorXY(); // As relayout of text may not be done at this point natural size is used to get size. Single line scrolling only.
- const float alignmentOffset = mController->GetAutoScrollLineAlignment();
+ const Size textNaturalSize = GetNaturalSize().GetVectorXY(); // As relayout of text may not be done at this point natural size is used to get size. Single line scrolling only.
const Text::CharacterDirection direction = mController->GetAutoScrollDirection();
- DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::SetUpAutoScrolling alignmentOffset[%f] offScreenSize[%f,%f] controlSize[%f,%f]\n",
- alignmentOffset, offScreenSize.x,offScreenSize.y , controlSize.x,controlSize.y );
+ DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::SetUpAutoScrolling textNaturalSize[%f,%f] controlSize[%f,%f]\n",
+ textNaturalSize.x,textNaturalSize.y , controlSize.x,controlSize.y );
if ( !mTextScroller )
{
// If speed, loopCount or gap not set via property system then will need to create a TextScroller with defaults
mTextScroller = Text::TextScroller::New( *this );
}
- mTextScroller->SetParameters( mRenderableActor, controlSize, offScreenSize, direction, alignmentOffset, mController->GetHorizontalAlignment() );
-
- Actor self = Self();
- self.Add( mTextScroller->GetScrollingText() );
- self.Add( mTextScroller->GetSourceCamera() );
-}
-void TextLabel::OnStageConnect( Dali::Actor actor )
-{
- if ( mHasBeenStaged )
- {
- RenderText();
- }
- else
- {
- mHasBeenStaged = true;
- }
+ // Create a texture of the text for scrolling
+ Text::TypesetterPtr typesetter = Text::Typesetter::New( mController->GetTextModel() );
+ PixelData data = typesetter->Render( textNaturalSize, Text::Typesetter::RENDER_TEXT_AND_STYLES, true, Pixel::RGBA8888 ); // ignore the horizontal alignment
+ Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D,
+ data.GetPixelFormat(),
+ data.GetWidth(),
+ data.GetHeight() );
+ texture.Upload( data );
+
+ TextureSet textureSet = TextureSet::New();
+ textureSet.SetTexture( 0u, texture );
+
+ // Filter mode needs to be set to linear to produce better quality while scaling.
+ Sampler sampler = Sampler::New();
+ sampler.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR );
+ textureSet.SetSampler( 0u, sampler );
+
+ // Set parameters for scrolling
+ Renderer renderer = static_cast<Internal::Visual::Base&>( GetImplementation( mVisual ) ).GetRenderer();
+ mTextScroller->SetParameters( Self(), renderer, textureSet, controlSize, textNaturalSize, direction, mController->GetHorizontalAlignment(), mController->GetVerticalAlignment() );
}
void TextLabel::ScrollingFinished()
TextLabel::TextLabel()
: Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
mRenderingBackend( DEFAULT_RENDERING_BACKEND ),
- mHasBeenStaged( false )
+ mTextUpdateNeeded( false )
{
}
*
*/
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/property-map.h>
+
// INTERNAL INCLUDES
#include <dali-toolkit/public-api/controls/control-impl.h>
#include <dali-toolkit/public-api/controls/text-controls/text-label.h>
#include <dali-toolkit/internal/text/text-scroller-interface.h>
#include <dali-toolkit/internal/text/rendering/text-renderer.h>
#include <dali-toolkit/internal/text/text-scroller.h>
+#include <dali-toolkit/internal/visuals/text/text-visual.h>
+
namespace Dali
{
TextLabel(const TextLabel&);
TextLabel& operator=(const TextLabel& rhs);
- // Connection needed to re-render text, when a Text Label returns to the stage
- void OnStageConnect( Dali::Actor actor );
-
- /**
- * @brief Render view, create and attach actor(s) to this Text Label
- */
- void RenderText();
-
/**
* @brief Set up Autoscrolling
*/
private: // Data
Text::ControllerPtr mController;
- Text::RendererPtr mRenderer;
Text::TextScrollerPtr mTextScroller;
- Actor mRenderableActor;
+
+ Toolkit::Visual::Base mVisual;
+
int mRenderingBackend;
- bool mHasBeenStaged:1;
+ bool mTextUpdateNeeded:1;
};
} // namespace Internal
// EXTERNAL INCLUDES
#include <dali/devel-api/text-abstraction/font-client.h>
#include <memory.h>
+#include <dali/public-api/common/constants.h>
// INTERNAL INCLUDES
#include <dali-toolkit/internal/text/rendering/view-model.h>
*/
struct GlyphData
{
- uint32_t* bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
+ Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
Vector2* position; ///< The position of the glyph.
TextAbstraction::FontClient::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
unsigned int width; ///< The bitmap's width.
* @param[in] data Struct which contains the glyph's data and the bitmap's data.
* @param[in] position The position of the glyph.
* @param[in] color The color of the glyph.
+ * @param[in] style The style of the text.
+ * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
*/
-void TypesetGlyph( const GlyphData& data,
+void TypesetGlyph( GlyphData& data,
const Vector2* const position,
- const Vector4* const color )
+ const Vector4* const color,
+ Typesetter::Style style,
+ Pixel::Format pixelFormat )
{
if( ( 0u == data.glyphBitmap.width ) || ( 0u == data.glyphBitmap.height ) )
{
const int widthMinusOne = static_cast<int>( data.width - 1u );
const int heightMinusOne = static_cast<int>( data.height - 1u );
- // Whether the given glyph is a color one.
- const bool isColorGlyph = Pixel::BGRA8888 == data.glyphBitmap.format;
+ if ( Pixel::RGBA8888 == pixelFormat )
+ {
+ // Whether the given glyph is a color one.
+ const bool isColorGlyph = Pixel::BGRA8888 == data.glyphBitmap.format;
- // Pointer to the color glyph if there is one.
- const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>( data.glyphBitmap.buffer ) : NULL;
+ // Pointer to the color glyph if there is one.
+ const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>( data.glyphBitmap.buffer ) : NULL;
- // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
- // The format is RGBA8888.
- uint32_t packedColor = 0u;
- uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>( &packedColor );
- *( packedColorBuffer + 2 ) = static_cast<uint8_t>( color->b * 255.f );
- *( packedColorBuffer + 1 ) = static_cast<uint8_t>( color->g * 255.f );
- *packedColorBuffer = static_cast<uint8_t>( color->r * 255.f );
+ // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
+ // The format is RGBA8888.
+ uint32_t packedColor = 0u;
+ uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>( &packedColor );
+ *( packedColorBuffer + 2 ) = static_cast<uint8_t>( color->b * 255.f );
+ *( packedColorBuffer + 1 ) = static_cast<uint8_t>( color->g * 255.f );
+ *packedColorBuffer = static_cast<uint8_t>( color->r * 255.f );
- // Initial vertical offset.
- const int yOffset = data.verticalOffset + position->y;
+ // Initial vertical offset.
+ const int yOffset = data.verticalOffset + position->y;
- // Traverse the pixels of the glyph line per line.
- for( int lineIndex = 0, glyphHeight = static_cast<int>( data.glyphBitmap.height ); lineIndex < glyphHeight; ++lineIndex )
- {
- const int yOffsetIndex = yOffset + lineIndex;
- if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
+ // Traverse the pixels of the glyph line per line.
+ for( int lineIndex = 0, glyphHeight = static_cast<int>( data.glyphBitmap.height ); lineIndex < glyphHeight; ++lineIndex )
{
- // Do not write out of bounds.
- break;
+ const int yOffsetIndex = yOffset + lineIndex;
+ if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
+ {
+ // Do not write out of bounds.
+ break;
+ }
+
+ const int verticalOffset = yOffsetIndex * data.width;
+ const int xOffset = data.horizontalOffset + position->x;
+ const int glyphBufferOffset = lineIndex * static_cast<int>( data.glyphBitmap.width );
+ for( int index = 0, glyphWidth = static_cast<int>( data.glyphBitmap.width ); index < glyphWidth; ++index )
+ {
+ const int xOffsetIndex = xOffset + index;
+ if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
+ {
+ // Don't write out of bounds.
+ break;
+ }
+
+ uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( data.bitmapBuffer.GetBuffer() );
+
+ if( isColorGlyph )
+ {
+ // Retrieves the color from the color glyph. The format is BGRA8888.
+ uint32_t packedColorGlyph = *( colorGlyphBuffer + glyphBufferOffset + index );
+ uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>( &packedColorGlyph );
+
+ if( Typesetter::STYLE_SHADOW == style )
+ {
+ // The shadow of color glyph needs to have the shadow color.
+ *( packedColorGlyphBuffer + 2 ) = static_cast<uint8_t>( color->b * 255.f );
+ *( packedColorGlyphBuffer + 1 ) = static_cast<uint8_t>( color->g * 255.f );
+ *packedColorGlyphBuffer = static_cast<uint8_t>( color->r * 255.f );
+ }
+ else
+ {
+ std::swap( *packedColorGlyphBuffer, *( packedColorGlyphBuffer + 2u ) ); // Swap B and R.
+ }
+
+ // Update the alpha channel.
+ if( Typesetter::STYLE_MASK == style )
+ {
+ // Create an alpha mask for color glyph.
+ *( packedColorGlyphBuffer + 3u ) = 0u;
+ }
+ else
+ {
+ *( packedColorGlyphBuffer + 3u ) = static_cast<uint8_t>( color->a * static_cast<float>( *( packedColorGlyphBuffer + 3u ) ) );
+ }
+
+ // Set the color into the final pixel buffer.
+ *( bitmapBuffer + verticalOffset + xOffsetIndex ) = packedColorGlyph;
+ }
+ else
+ {
+ // Update the alpha channel.
+ const uint8_t alpha = *( data.glyphBitmap.buffer + glyphBufferOffset + index );
+
+ // Copy non-transparent pixels only
+ if ( alpha > 0u )
+ {
+ // Check alpha of overlapped pixels
+ uint32_t& currentColor = *( bitmapBuffer + verticalOffset + xOffsetIndex );
+ uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>( ¤tColor );
+
+ uint8_t currentAlpha = *( packedCurrentColorBuffer + 3u );
+ uint8_t newAlpha = static_cast<uint8_t>( color->a * static_cast<float>( alpha ) );
+
+ // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
+ // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
+ // semi-transparent gaps between joint glyphs with overlapped pixels, which could
+ // happen, for example, in the RTL text when we copy glyphs from right to left).
+ *( packedColorBuffer + 3u ) = std::max( currentAlpha, newAlpha );
+
+ // Set the color into the final pixel buffer.
+ currentColor = packedColor;
+ }
+ }
+ }
}
+ }
+ else
+ {
+ // Initial vertical offset.
+ const int yOffset = data.verticalOffset + position->y;
- const int verticalOffset = yOffsetIndex * data.width;
- const int xOffset = data.horizontalOffset + position->x;
- const int glyphBufferOffset = lineIndex * static_cast<int>( data.glyphBitmap.width );
- for( int index = 0, glyphWidth = static_cast<int>( data.glyphBitmap.width ); index < glyphWidth; ++index )
+ // Traverse the pixels of the glyph line per line.
+ for( int lineIndex = 0, glyphHeight = static_cast<int>( data.glyphBitmap.height ); lineIndex < glyphHeight; ++lineIndex )
{
- const int xOffsetIndex = xOffset + index;
- if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
+ const int yOffsetIndex = yOffset + lineIndex;
+ if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
{
- // Don't write out of bounds.
+ // Do not write out of bounds.
break;
}
- if( isColorGlyph )
+ const int verticalOffset = yOffsetIndex * data.width;
+ const int xOffset = data.horizontalOffset + position->x;
+ const int glyphBufferOffset = lineIndex * static_cast<int>( data.glyphBitmap.width );
+ for( int index = 0, glyphWidth = static_cast<int>( data.glyphBitmap.width ); index < glyphWidth; ++index )
{
- // Retrieves the color from the glyph. The format is BGRA8888.
- uint32_t packedColorGlyph = *( colorGlyphBuffer + glyphBufferOffset + index );
+ const int xOffsetIndex = xOffset + index;
+ if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
+ {
+ // Don't write out of bounds.
+ break;
+ }
- // Update the alpha channel.
- uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>( &packedColorGlyph );
- std::swap( *packedColorGlyphBuffer, *( packedColorGlyphBuffer + 2u ) ); // Swap B and R.
- *( packedColorGlyphBuffer + 3u ) = static_cast<uint8_t>( color->a * static_cast<float>( *( packedColorGlyphBuffer + 3u ) ) );
+ uint8_t* bitmapBuffer = reinterpret_cast< uint8_t* >( data.bitmapBuffer.GetBuffer() );
- // Set the color into the final pixel buffer.
- *( data.bitmapBuffer + verticalOffset + xOffsetIndex ) = packedColorGlyph;
- }
- else
- {
// Update the alpha channel.
const uint8_t alpha = *( data.glyphBitmap.buffer + glyphBufferOffset + index );
- *( packedColorBuffer + 3u ) = static_cast<uint8_t>( color->a * static_cast<float>( alpha ) );
- // Set the color into the final pixel buffer.
- *( data.bitmapBuffer + verticalOffset + xOffsetIndex ) = packedColor;
+ // Copy non-transparent pixels only
+ if ( alpha > 0u )
+ {
+ // Check alpha of overlapped pixels
+ uint8_t& currentAlpha = *( bitmapBuffer + verticalOffset + xOffsetIndex );
+ uint8_t newAlpha = static_cast<uint8_t>( color->a * static_cast<float>( alpha ) );
+// printf("y: %d, x: %d: alpha: %u, currentAlpha: %u, newAlpha: %u, a: %u\n", yOffsetIndex, xOffsetIndex, alpha, currentAlpha, newAlpha, std::max( currentAlpha, newAlpha ) );
+
+ // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
+ // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
+ // semi-transparent gaps between joint glyphs with overlapped pixels, which could
+ // happen, for example, in the RTL text when we copy glyphs from right to left).
+ *( bitmapBuffer + verticalOffset + xOffsetIndex ) = std::max( currentAlpha, newAlpha );
+ }
}
}
}
}
+bool IsGlyphUnderlined( GlyphIndex index,
+ const Vector<GlyphRun>& underlineRuns )
+{
+ for( Vector<GlyphRun>::ConstIterator it = underlineRuns.Begin(),
+ endIt = underlineRuns.End();
+ it != endIt;
+ ++it )
+ {
+ const GlyphRun& run = *it;
+
+ if( ( run.glyphIndex <= index ) && ( index < run.glyphIndex + run.numberOfGlyphs ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
} // namespace
TypesetterPtr Typesetter::New( const ModelInterface* const model )
return mModel;
}
-PixelData Typesetter::Render( const Vector2& size )
+PixelData Typesetter::Render( const Vector2& size, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat )
{
// @todo. This initial implementation for a TextLabel has only one visible page.
}
}
+ // Generate the image buffers of the text for each different style first,
+ // then combine all of them together as one final image buffer. We try to
+ // do all of these in CPU only, so that once the final texture is generated,
+ // no calculation is needed in GPU during each frame.
+
+ const unsigned int bufferWidth = static_cast<unsigned int>( size.width );
+ const unsigned int bufferHeight = static_cast<unsigned int>( size.height );
+
+ const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
+ const unsigned int bufferSizeChar = 4u * bufferSizeInt;
+
+ Length numberOfGlyphs = mModel->GetNumberOfGlyphs();
+
+ Devel::PixelBuffer imageBuffer;
+
+ if( RENDER_MASK == behaviour )
+ {
+ // Generate the image buffer as an alpha mask for color glyphs.
+ imageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penY, 0u, numberOfGlyphs - 1 );
+ }
+ else if( RENDER_NO_TEXT == behaviour )
+ {
+ // Generate an empty image buffer so that it can been combined with the image buffers for styles
+ imageBuffer = Devel::PixelBuffer::New( bufferWidth, bufferHeight, Pixel::RGBA8888 );
+ memset( imageBuffer.GetBuffer(), 0u, bufferSizeChar );
+ }
+ else
+ {
+ // Generate the image buffer for the text with no style.
+ imageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penY, 0u, numberOfGlyphs -1 );
+ }
+
+ if ( ( RENDER_NO_STYLES != behaviour ) && ( RENDER_MASK != behaviour ) )
+ {
+ // @todo. Support shadow and underline for partial text later on.
+
+ // Generate the shadow if enabled
+ const Vector2& shadowOffset = mModel->GetShadowOffset();
+ if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 )
+ {
+ // Create the image buffer for shadow
+ Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penY, 0u, numberOfGlyphs - 1 );
+
+ // Combine the two buffers
+ imageBuffer = CombineImageBuffer( imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight );
+ }
+
+ // Generate the underline if enabled
+ const bool underlineEnabled = mModel->IsUnderlineEnabled();
+ if ( underlineEnabled )
+ {
+ // Create the image buffer for underline
+ Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penY, 0u, numberOfGlyphs - 1 );
+
+ // Combine the two buffers
+ imageBuffer = CombineImageBuffer( imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight );
+ }
+ }
+
+ // Create the final PixelData for the combined image buffer
+ PixelData pixelData = Devel::PixelBuffer::Convert( imageBuffer );
+
+ return pixelData;
+}
+
+Devel::PixelBuffer Typesetter::CreateImageBuffer( const unsigned int bufferWidth, const unsigned int bufferHeight, Typesetter::Style style, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int verticalOffset, GlyphIndex fromGlyphIndex, GlyphIndex toGlyphIndex )
+{
// Retrieve lines, glyphs, positions and colors from the view model.
const Length modelNumberOfLines = mModel->GetNumberOfLines();
const LineRun* const modelLinesBuffer = mModel->GetLines();
const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
// Whether to use the default color.
- const bool useDefaultColor = NULL == colorsBuffer;
+ const bool useDefaultColor = ( NULL == colorsBuffer );
const Vector4& defaultColor = mModel->GetDefaultColor();
// Create and initialize the pixel buffer.
GlyphData glyphData;
- glyphData.verticalOffset = penY;
-
- glyphData.width = static_cast<unsigned int>( size.width );
- glyphData.height = static_cast<unsigned int>( size.height );
- const unsigned int bufferSizeInt = glyphData.width * glyphData.height;
- const unsigned int bufferSizeChar = 4u * bufferSizeInt;
- glyphData.bitmapBuffer = new uint32_t[ bufferSizeInt ]; // This array will get deleted by PixelData because of the DELETE_ARRAY parameter.
- memset( glyphData.bitmapBuffer, 0u, bufferSizeChar );
+ glyphData.verticalOffset = verticalOffset;
+ glyphData.width = bufferWidth;
+ glyphData.height = bufferHeight;
+ glyphData.bitmapBuffer = Devel::PixelBuffer::New( bufferWidth, bufferHeight, pixelFormat );
- PixelData pixelData = PixelData::New( reinterpret_cast<uint8_t*>( glyphData.bitmapBuffer ),
- bufferSizeChar,
- glyphData.width,
- glyphData.height,
- Pixel::RGBA8888, // The format is RGBA8888 because is the format accepted by the image atlas manager.
- PixelData::DELETE_ARRAY );
+ if ( Pixel::RGBA8888 == pixelFormat )
+ {
+ const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
+ const unsigned int bufferSizeChar = 4u * bufferSizeInt;
+ memset( glyphData.bitmapBuffer.GetBuffer(), 0u, bufferSizeChar );
+ }
+ else
+ {
+ memset( glyphData.bitmapBuffer.GetBuffer(), 0, bufferWidth * bufferHeight );
+ }
// Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
const LineRun& line = *( modelLinesBuffer + lineIndex );
// Sets the horizontal offset of the line.
- glyphData.horizontalOffset = static_cast<int>( line.alignmentOffset );
+ glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>( line.alignmentOffset );
// Increases the vertical offset with the line's ascender.
glyphData.verticalOffset += static_cast<int>( line.ascender );
+ if ( style == Typesetter::STYLE_SHADOW )
+ {
+ const Vector2& shadowOffset = mModel->GetShadowOffset();
+ glyphData.horizontalOffset += shadowOffset.x;
+ if ( lineIndex == 0u )
+ {
+ // Only need to add the vertical shadow offset for once
+ glyphData.verticalOffset += shadowOffset.y;
+ }
+ }
+
+ const bool underlineEnabled = mModel->IsUnderlineEnabled();
+ const Vector4& underlineColor = mModel->GetUnderlineColor();
+ const float underlineHeight = mModel->GetUnderlineHeight();
+
+ // Get the underline runs.
+ const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
+ Vector<GlyphRun> underlineRuns;
+ underlineRuns.Resize( numberOfUnderlineRuns );
+ mModel->GetUnderlineRuns( underlineRuns.Begin(), 0u, numberOfUnderlineRuns );
+
+ bool thereAreUnderlinedGlyphs = false;
+
+ float currentUnderlinePosition = 0.0f;
+ float currentUnderlineThickness = underlineHeight;
+ float maxUnderlineThickness = currentUnderlineThickness;
+
+ FontId lastUnderlinedFontId = 0;
+
+ float lineExtentLeft = bufferWidth;
+ float lineExtentRight = 0.0f;
+ float baseline = 0.0f;
+
// Traverses the glyphs of the line.
const GlyphIndex endGlyphIndex = std::min( numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs );
for( GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex )
{
+ if ( glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex )
+ {
+ // Ignore any glyph that out of the specified range
+ continue;
+ }
+
// Retrieve the glyph's info.
const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
continue;
}
+ const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined( glyphIndex, underlineRuns );
+ thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
+
+ // Are we still using the same fontId as previous
+ if( underlineGlyph && ( glyphInfo->fontId != lastUnderlinedFontId ) )
+ {
+ // We need to fetch fresh font underline metrics
+ FontMetrics fontMetrics;
+ fontClient.GetFontMetrics( glyphInfo->fontId, fontMetrics );
+ currentUnderlinePosition = ceil( fabsf( fontMetrics.underlinePosition ) );
+ const float descender = ceil( fabsf( fontMetrics.descender ) );
+
+ if( fabsf( underlineHeight ) < Math::MACHINE_EPSILON_1000 )
+ {
+ currentUnderlineThickness = fontMetrics.underlineThickness;
+
+ // Ensure underline will be at least a pixel high
+ if ( currentUnderlineThickness < 1.0f )
+ {
+ currentUnderlineThickness = 1.0f;
+ }
+ else
+ {
+ currentUnderlineThickness = ceil( currentUnderlineThickness );
+ }
+ }
+
+ // The underline thickness should be the max underline thickness of all glyphs of the line.
+ if ( currentUnderlineThickness > maxUnderlineThickness )
+ {
+ maxUnderlineThickness = currentUnderlineThickness;
+ }
+
+ // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
+ if( currentUnderlinePosition > descender )
+ {
+ currentUnderlinePosition = descender;
+ }
+
+ if( fabsf( currentUnderlinePosition ) < Math::MACHINE_EPSILON_1000 )
+ {
+ // Move offset down by one ( EFL behavior )
+ currentUnderlinePosition = 1.0f;
+ }
+
+ lastUnderlinedFontId = glyphInfo->fontId;
+ } // underline
+
// Retrieves the glyph's position.
const Vector2* const position = positionBuffer + glyphIndex;
+ if ( baseline < position->y + glyphInfo->yBearing )
+ {
+ baseline = position->y + glyphInfo->yBearing;
+ }
+
+ // Calculate the positions of leftmost and rightmost glyphs in the current line
+ if ( position->x < lineExtentLeft)
+ {
+ lineExtentLeft = position->x;
+ }
+
+ if ( position->x + glyphInfo->width > lineExtentRight)
+ {
+ lineExtentRight = position->x + glyphInfo->width;
+ }
// Retrieves the glyph's color.
const ColorIndex colorIndex = *( colorIndexBuffer + glyphIndex );
- const Vector4* const color = ( useDefaultColor || ( 0u == colorIndex ) ) ? &defaultColor : colorsBuffer + ( colorIndex - 1u );
+
+ const Vector4* color;
+ if ( style == Typesetter::STYLE_SHADOW )
+ {
+ color = &( mModel->GetShadowColor() );
+ }
+ else
+ {
+ color = ( useDefaultColor || ( 0u == colorIndex ) ) ? &defaultColor : colorsBuffer + ( colorIndex - 1u );
+ }
// Retrieves the glyph's bitmap.
glyphData.glyphBitmap.buffer = NULL;
{
TypesetGlyph( glyphData,
position,
- color );
+ color,
+ style,
+ pixelFormat);
// delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
delete []glyphData.glyphBitmap.buffer;
glyphData.glyphBitmap.buffer = NULL;
+ }
+ }
+ // Draw the underline from the leftmost glyph to the rightmost glyph
+ if ( thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE )
+ {
+ int underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
+
+ for( unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineThickness; y++ )
+ {
+ if( ( y < 0 ) || ( y > bufferHeight - 1 ) )
+ {
+ // Do not write out of bounds.
+ break;
+ }
+
+ for( unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++ )
+ {
+ if( ( x < 0 ) || ( x > bufferWidth - 1 ) )
+ {
+ // Do not write out of bounds.
+ break;
+ }
+
+ // Always RGBA image for text with styles
+ uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( glyphData.bitmapBuffer.GetBuffer() );
+ uint32_t underlinePixel = *( bitmapBuffer + y * glyphData.width + x );
+ uint8_t* underlinePixelBuffer = reinterpret_cast<uint8_t*>( &underlinePixel );
+
+ // Write the underline color to the pixel buffer
+ *( underlinePixelBuffer ) = static_cast<uint8_t>( underlineColor.r * 255.f );
+ *( underlinePixelBuffer + 1u ) = static_cast<uint8_t>( underlineColor.g * 255.f );
+ *( underlinePixelBuffer + 2u ) = static_cast<uint8_t>( underlineColor.b * 255.f );
+ *( underlinePixelBuffer + 3u ) = static_cast<uint8_t>( underlineColor.a * 255.f );
+
+ *( bitmapBuffer + y * glyphData.width + x ) = underlinePixel;
+ }
}
}
glyphData.verticalOffset += static_cast<int>( -line.descender );
}
- return pixelData;
+ return glyphData.bitmapBuffer;
+}
+
+Devel::PixelBuffer Typesetter::CombineImageBuffer( Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight )
+{
+ unsigned char* topBuffer = topPixelBuffer.GetBuffer();
+ unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
+
+ Devel::PixelBuffer combinedPixelBuffer;
+
+ if ( topBuffer == NULL && bottomBuffer == NULL )
+ {
+ // Nothing to do if both buffers are empty.
+ return combinedPixelBuffer;
+ }
+
+ if ( topBuffer == NULL )
+ {
+ // Nothing to do if topBuffer is empty.
+ return bottomPixelBuffer;
+ }
+
+ if ( bottomBuffer == NULL )
+ {
+ // Nothing to do if bottomBuffer is empty.
+ return topPixelBuffer;
+ }
+
+ // Always combine two RGBA images
+ const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
+ const unsigned int bufferSizeChar = 4u * bufferSizeInt;
+
+ combinedPixelBuffer = Devel::PixelBuffer::New( bufferWidth, bufferHeight, Pixel::RGBA8888 );
+ uint8_t* combinedBuffer = reinterpret_cast< uint8_t* >( combinedPixelBuffer.GetBuffer() );
+ memset( combinedBuffer, 0u, bufferSizeChar );
+
+ for (unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
+ {
+ // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
+ // Otherwise, copy pixel from topBuffer to combinedBuffer.
+
+ unsigned int alphaBuffer1 = topBuffer[pixelIndex*4+3];
+ unsigned int alphaBuffer2 = bottomBuffer[pixelIndex*4+3];
+
+ if ( alphaBuffer1 != 255 || alphaBuffer2 != 255 )
+ {
+ // At least one pixel is not fully opaque
+ // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
+ combinedBuffer[pixelIndex*4] = ( topBuffer[pixelIndex*4] * topBuffer[pixelIndex*4+3] / 255 ) + ( bottomBuffer[pixelIndex*4] * bottomBuffer[pixelIndex*4+3] * ( 255 - topBuffer[pixelIndex*4+3] ) / ( 255*255 ) );
+ combinedBuffer[pixelIndex*4+1] = ( topBuffer[pixelIndex*4+1] * topBuffer[pixelIndex*4+3] / 255 ) + ( bottomBuffer[pixelIndex*4+1] * bottomBuffer[pixelIndex*4+3] * ( 255 - topBuffer[pixelIndex*4+3] ) / ( 255*255 ) );
+ combinedBuffer[pixelIndex*4+2] = ( topBuffer[pixelIndex*4+2] * topBuffer[pixelIndex*4+3] / 255 ) + ( bottomBuffer[pixelIndex*4+2] * bottomBuffer[pixelIndex*4+3] * ( 255 - topBuffer[pixelIndex*4+3] ) / ( 255*255 ) );
+ combinedBuffer[pixelIndex*4+3] = topBuffer[pixelIndex*4+3] + ( bottomBuffer[pixelIndex*4+3] * ( 255 - topBuffer[pixelIndex*4+3] ) / 255 );
+ }
+ else
+ {
+ // Copy the pixel from topBuffer to combinedBuffer
+ combinedBuffer[pixelIndex*4] = topBuffer[pixelIndex*4];
+ combinedBuffer[pixelIndex*4+1] = topBuffer[pixelIndex*4+1];
+ combinedBuffer[pixelIndex*4+2] = topBuffer[pixelIndex*4+2];
+ combinedBuffer[pixelIndex*4+3] = topBuffer[pixelIndex*4+3];
+ }
+ }
+
+ return combinedPixelBuffer;
}
Typesetter::Typesetter( const ModelInterface* const model )
// EXTERNAL INCLUDES
#include <dali/public-api/common/intrusive-ptr.h>
#include <dali/public-api/object/ref-object.h>
+#include <dali/public-api/images/pixel.h>
#include <dali/public-api/images/pixel-data.h>
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
namespace Dali
{
*/
class Typesetter : public RefObject
{
+public:
+
+ /**
+ * @brief Behaviours of how to render the text.
+ */
+ enum RenderBehaviour
+ {
+ RENDER_TEXT_AND_STYLES, ///< Render both the text and its styles
+ RENDER_NO_TEXT, ///< Do not render the text itself
+ RENDER_NO_STYLES, ///< Do not render any styles
+ RENDER_MASK ///< Render an alpha mask (for color glyphs with no color animation, e.g. emoji)
+ };
+
+ /**
+ * @brief Styles of the text.
+ */
+ enum Style
+ {
+ STYLE_NONE, ///< No style
+ STYLE_MASK, ///< Alpha mask
+ STYLE_SHADOW, ///< Hard shadow
+ STYLE_SOFT_SHADOW, ///< Soft shadow
+ STYLE_UNDERLINE, ///< Underline
+ STYLE_OUTLINE, ///< Outline
+ STYLE_BACKGROUND ///< Text background
+ };
+
public: // Constructor.
/**
* @brief Creates a Typesetter instance.
* Does the following operations:
* - Finds the visible pages needed to be rendered.
* - Elide glyphs if needed.
- * - Retrieves the data buffers from the text model.
- * - Creates the pixel data used to generate the final image with the given size.
- * - Traverse the visible glyphs, retrieve their bitmaps and compose the final pixel data.
+ * - Creates image buffers for diffrent text styles with the given size.
+ * - Combines different image buffers to create the pixel data used to generate the final image
*
* @param[in] size The renderer size.
+ * @param[in] behaviour The behaviour of how to render the text (i.e. whether to render the text only or the styles only or both).
+ * @param[in] ignoreHorizontalAlignment Whether to ignore the horizontal alignment (i.e. always render as if HORIZONTAL_ALIGN_BEGIN).
+ * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
*
* @return A pixel data with the text rendered.
*/
- PixelData Render( const Vector2& size );
+ PixelData Render( const Vector2& size, RenderBehaviour behaviour = RENDER_TEXT_AND_STYLES, bool ignoreHorizontalAlignment = false, Pixel::Format pixelFormat = Pixel::RGBA8888 );
private:
/**
// Declared private and left undefined to avoid copies.
Typesetter& operator=( const Typesetter& handle );
+ /**
+ * @brief Create the image buffer for the given range of the glyphs in the given style.
+ *
+ * Does the following operations:
+ * - Retrieves the data buffers from the text model.
+ * - Creates the pixel data used to generate the final image with the given size.
+ * - Traverse the visible glyphs, retrieve their bitmaps and compose the final pixel data.
+ *
+ * @param[in] bufferWidth The width of the image buffer.
+ * @param[in] bufferHeight The height of the image buffer.
+ * @param[in] style The style of the text.
+ * @param[in] ignoreHorizontalAlignment Whether to ignore the horizontal alignment, not ignored by default.
+ * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
+ * @param[in] verticalOffset The vertical offset to be added to the glyph's position.
+ * @param[in] fromGlyphIndex The index of the first glyph within the text to be drawn
+ * @param[in] toGlyphIndex The index of the last glyph within the text to be drawn
+ *
+ * @return An image buffer with the text.
+ */
+ Devel::PixelBuffer CreateImageBuffer( const unsigned int bufferWidth, const unsigned int bufferHeight, Typesetter::Style style, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int verticalOffset, TextAbstraction::GlyphIndex fromGlyphIndex, TextAbstraction::GlyphIndex toGlyphIndex );
+
+ /**
+ * @brief Combine the two RGBA image buffers together.
+ *
+ * The top layer buffer will blend over the bottom layer buffer:
+ * - If the pixel is not fully opaque from either buffer, it will be blended with
+ * the pixel from the other buffer and copied to the combined buffer.
+ * - If the pixels from both buffers are fully opaque, the pixels from the top layer
+ * buffer will be copied to the combined buffer.
+ *
+ * @param[in] topPixelBuffer The top layer buffer.
+ * @param[in] bottomPixelBuffer The bottom layer buffer.
+ * @param[in] bufferWidth The width of the image buffer.
+ * @param[in] bufferHeight The height of the image buffer.
+ *
+ * @return The combined image buffer with the text.
+ *
+ */
+ Devel::PixelBuffer CombineImageBuffer( Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeightbool );
+
protected:
/**
virtual ~Typesetter();
private:
+
ViewModel* mModel;
};
return mModel->GetLines();
}
+Length ViewModel::GetNumberOfScripts() const
+{
+ return mModel->GetNumberOfScripts();
+}
+
+const ScriptRun* const ViewModel::GetScriptRuns() const
+{
+ return mModel->GetScriptRuns();
+}
+
Length ViewModel::GetNumberOfGlyphs() const
{
if( mIsTextElided && mModel->IsTextElideEnabled() )
return mModel->GetDefaultColor();
}
+const Vector2& ViewModel::GetShadowOffset() const
+{
+ return mModel->GetShadowOffset();
+}
+
+const Vector4& ViewModel::GetShadowColor() const
+{
+ return mModel->GetShadowColor();
+}
+
+const Vector4& ViewModel::GetUnderlineColor() const
+{
+ return mModel->GetUnderlineColor();
+}
+
+bool ViewModel::IsUnderlineEnabled() const
+{
+ return mModel->IsUnderlineEnabled();
+}
+
+float ViewModel::GetUnderlineHeight() const
+{
+ return mModel->GetUnderlineHeight();
+}
+
+Length ViewModel::GetNumberOfUnderlineRuns() const
+{
+ return mModel->GetNumberOfUnderlineRuns();
+}
+
+void ViewModel::GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const
+{
+ mModel->GetUnderlineRuns( underlineRuns, index, numberOfRuns );
+}
+
void ViewModel::ElideGlyphs()
{
mIsTextElided = false;
*/
virtual const LineRun* const GetLines() const;
+ /**
+ * @copydoc ModelInterface::GetNumberOfScripts()
+ */
+ virtual Length GetNumberOfScripts() const;
+
+ /**
+ * @copydoc ModelInterface::GetScriptRuns()
+ */
+ virtual const ScriptRun* const GetScriptRuns() const;
+
/**
* @copydoc ModelInterface::GetNumberOfGlyphs()
*/
*/
virtual const Vector4& GetDefaultColor() const;
+ /**
+ * @copydoc ModelInterface::GetShadowOffset()
+ */
+ virtual const Vector2& GetShadowOffset() const;
+
+ /**
+ * @copydoc ModelInterface::GetShadowColor()
+ */
+ virtual const Vector4& GetShadowColor() const;
+
+ /**
+ * @copydoc ModelInterface::GetUnderlineColor()
+ */
+ virtual const Vector4& GetUnderlineColor() const;
+
+ /**
+ * @copydoc ModelInterface::IsUnderlineEnabled()
+ */
+ virtual bool IsUnderlineEnabled() const;
+
+ /**
+ * @copydoc ModelInterface::GetUnderlineHeight()
+ */
+ virtual float GetUnderlineHeight() const;
+
+ /**
+ * @copydoc ModelInterface::GetNumberOfUnderlineRuns()
+ */
+ virtual Length GetNumberOfUnderlineRuns() const;
+
+ /**
+ * @copydoc ModelInterface::GetUnderlineRuns()
+ */
+ virtual void GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const;
+
/**
* @brief Does the text elide.
*
mImpl->UpdateModel( onlyOnceOperations );
// Layout the text for the new width.
- mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT );
+ mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT | REORDER );
// Store the actual control's size to restore later.
const Size actualControlSize = mImpl->mModel->mVisualModel->mControlSize;
DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
static_cast<OperationsMask>( onlyOnceOperations |
- LAYOUT ),
+ LAYOUT | REORDER ),
naturalSize.GetVectorXY() );
// Do not do again the only once operations.
}
}
+void Controller::SetControlInterface( ControlInterface* controlInterface )
+{
+ mImpl->mControlInterface = controlInterface;
+}
+
bool Controller::ShouldClearFocusOnEscape() const
{
return mImpl->mShouldClearFocusOnEscape;
*/
const std::string& GetInputOutlineProperties() const;
+ /**
+ * @brief Set the control's interface.
+ *
+ * @param[in] controlInterface The control's interface.
+ */
+ void SetControlInterface( ControlInterface* controlInterface );
+
public: // Queries & retrieves.
/**
// INTERNAL INCLUDES
#include <dali-toolkit/internal/text/layouts/layout-alignment.h>
#include <dali-toolkit/internal/text/line-run.h>
+#include <dali-toolkit/internal/text/script-run.h>
#include <dali-toolkit/internal/text/text-definitions.h>
namespace Dali
*/
virtual const LineRun* const GetLines() const = 0;
+ /**
+ * @brief Retrieves the number of script runs.
+ *
+ * @return The number of script runs.
+ */
+ virtual Length GetNumberOfScripts() const = 0;
+
+ /**
+ * @brief Retrieves the script runs.
+ *
+ * @return A pointer to the vector with the runs of characters with the same script..
+ */
+ virtual const ScriptRun* const GetScriptRuns() const = 0;
+
/**
* @brief Retrieves the number of laid-out glyphs.
*
* @return The default color.
*/
virtual const Vector4& GetDefaultColor() const = 0;
+
+ /**
+ * @brief Retrieves the shadow offset, 0 indicates no shadow.
+ *
+ * @return The shadow offset.
+ */
+ virtual const Vector2& GetShadowOffset() const = 0;
+
+ /**
+ * @brief Retrieves the shadow color.
+ *
+ * @return The shadow color.
+ */
+ virtual const Vector4& GetShadowColor() const = 0;
+
+ /**
+ * @brief Retrieves the underline color.
+ *
+ * @return The underline color.
+ */
+ virtual const Vector4& GetUnderlineColor() const = 0;
+
+ /**
+ * @brief Returns whether underline is enabled or not.
+ *
+ * @return The underline state.
+ */
+ virtual bool IsUnderlineEnabled() const = 0;
+
+ /**
+ * @brief Retrieves the underline height override
+ *
+ * @return Returns the override height for an underline, 0 indicates that adaptor will determine the height
+ */
+ virtual float GetUnderlineHeight() const = 0;
+
+ /**
+ * @brief Retrieves the number of underline runs.
+ *
+ * @return The number of underline runs.
+ */
+ virtual Length GetNumberOfUnderlineRuns() const = 0;
+
+ /**
+ * @brief Retrieves the underline runs.
+ *
+ * @param[out] underlineRuns Pointer to a buffer where the underline runs are copied.
+ * @param[in] index Index of the first underline run to be copied.
+ * @param[in] numberOfRuns Number of underline runs to be copied.
+ */
+ virtual void GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const = 0;
};
} // namespace Text
return mVisualModel->mLines.Begin();
}
+Length Model::GetNumberOfScripts() const
+{
+ return mLogicalModel->mScriptRuns.Count();
+}
+
+const ScriptRun* const Model::GetScriptRuns() const
+{
+ return mLogicalModel->mScriptRuns.Begin();
+}
+
Length Model::GetNumberOfGlyphs() const
{
return mVisualModel->mGlyphs.Count();
return mVisualModel->mTextColor;
}
+const Vector2& Model::GetShadowOffset() const
+{
+ return mVisualModel->mShadowOffset;
+}
+
+const Vector4& Model::GetShadowColor() const
+{
+ return mVisualModel->mShadowColor;
+}
+
+const Vector4& Model::GetUnderlineColor() const
+{
+ return mVisualModel->GetUnderlineColor();
+}
+
+bool Model::IsUnderlineEnabled() const
+{
+ return mVisualModel->IsUnderlineEnabled();
+}
+
+float Model::GetUnderlineHeight() const
+{
+ return mVisualModel->GetUnderlineHeight();
+}
+
+Length Model::GetNumberOfUnderlineRuns() const
+{
+ return mVisualModel->GetNumberOfUnderlineRuns();
+}
+
+void Model::GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const
+{
+ mVisualModel->GetUnderlineRuns( underlineRuns, index, numberOfRuns );
+}
+
Model::Model()
: mLogicalModel(),
mVisualModel(),
*/
virtual const LineRun* const GetLines() const;
+ /**
+ * @copydoc ModelInterface::GetNumberOfScripts()
+ */
+ virtual Length GetNumberOfScripts() const;
+
+ /**
+ * @copydoc ModelInterface::GetScriptRuns()
+ */
+ virtual const ScriptRun* const GetScriptRuns() const;
+
/**
* @copydoc ModelInterface::GetNumberOfGlyphs()
*/
*/
virtual const Vector4& GetDefaultColor() const;
+ /**
+ * @copydoc ModelInterface::GetShadowOffset()
+ */
+ virtual const Vector2& GetShadowOffset() const;
+
+ /**
+ * @copydoc ModelInterface::GetShadowColor()
+ */
+ virtual const Vector4& GetShadowColor() const;
+
+ /**
+ * @copydoc ModelInterface::GetUnderlineColor()
+ */
+ virtual const Vector4& GetUnderlineColor() const;
+
+ /**
+ * @copydoc ModelInterface::IsUnderlineEnabled()
+ */
+ virtual bool IsUnderlineEnabled() const;
+
+ /**
+ * @copydoc ModelInterface::GetUnderlineHeight()
+ */
+ virtual float GetUnderlineHeight() const;
+
+ /**
+ * @copydoc ModelInterface::GetNumberOfUnderlineRuns()
+ */
+ virtual Length GetNumberOfUnderlineRuns() const;
+
+ /**
+ * @copydoc ModelInterface::GetUnderlineRuns()
+ */
+ virtual void GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const;
+
private: // Private contructors & copy operator.
/**
// EXTERNAL INCLUDES
#include <dali/public-api/common/stage.h>
-#include <dali/public-api/images/frame-buffer-image.h>
-#include <dali/public-api/render-tasks/render-task-list.h>
-#include <dali/public-api/rendering/geometry.h>
-#include <dali/public-api/rendering/renderer.h>
-#include <dali/public-api/rendering/sampler.h>
-#include <dali/public-api/rendering/shader.h>
-#include <dali/devel-api/images/texture-set-image.h>
#include <dali/integration-api/debug.h>
// INTERNAL INCLUDES
attribute mediump vec2 aPosition;\n
varying highp vec2 vTexCoord;\n
varying highp float vRatio;\n
- uniform mediump mat4 uModelMatrix;\n
- uniform mediump mat4 uViewMatrix;\n
- uniform mediump mat4 uProjection;\n
uniform mediump vec3 uSize;\n
uniform mediump float uDelta;\n
- uniform mediump vec2 uTextureSize;
+ uniform mediump vec2 uTextureSize;\n
uniform mediump float uGap;\n
- uniform mediump float uAlign;\n
+ uniform mediump float uHorizontalAlign;\n
+ uniform mediump float uVerticalAlign;\n
+ \n
+ uniform mediump mat4 uModelMatrix;\n
+ uniform mediump mat4 uViewMatrix;\n
+ uniform mediump mat4 uProjection;\n
\n
+ //Visual size and offset
+ uniform mediump vec2 offset;\n
+ uniform mediump vec2 size;\n
+ uniform mediump vec4 offsetSizeMode;\n
+ uniform mediump vec2 origin;\n
+ uniform mediump vec2 anchorPoint;\n
+
void main()\n
{\n
- {\n
- highp vec4 vertexPosition = vec4(aPosition*uSize.xy, 0.0, 1.0);\n
- vertexPosition = uViewMatrix * uModelMatrix * vertexPosition ;\n
- vertexPosition.x = floor( vertexPosition.x ) + 0.5;
- vertexPosition.y = floor( vertexPosition.y ) + 0.5;
- float smallTextPadding = max(uSize.x - uTextureSize.x, 0. );\n
- float gap = max( uGap, smallTextPadding );\n
- float delta = floor ( uDelta ) + 0.5;
- vTexCoord.x = ( delta + ( uAlign * ( uTextureSize.x - uSize.x ) ) + ( aPosition.x * uSize.x ) )/ ( uTextureSize.x + gap );\n
- vTexCoord.y = ( 0.5 + floor( aPosition.y * uSize.y ) )/ ( uTextureSize.y ) ;\n
- vRatio = uTextureSize.x / ( uTextureSize.x + gap );\n
- gl_Position = uProjection * vertexPosition;
- }\n
+ mediump vec2 visualOffset = mix( offset, offset/uSize.xy, offsetSizeMode.xy );\n
+ mediump vec2 visualSize = mix( uSize.xy * size, size, offsetSizeMode.zw );\n
+ \n
+ mediump float smallTextPadding = max( visualSize.x - uTextureSize.x, 0. );\n
+ mediump float gap = max( uGap, smallTextPadding );\n
+ mediump float delta = floor ( uDelta ) + 0.5;\n
+ vTexCoord.x = ( delta + uHorizontalAlign * ( uTextureSize.x - visualSize.x ) + floor( aPosition.x * visualSize.x ) + 0.5 - gap * 0.5 ) / ( uTextureSize.x + gap ) + 0.5;\n
+ vTexCoord.y = ( uVerticalAlign * ( uTextureSize.y - visualSize.y ) + floor( aPosition.y * visualSize.y ) + 0.5 ) / ( uTextureSize.y ) + 0.5;\n
+ vRatio = uTextureSize.x / ( uTextureSize.x + gap );\n
+ \n
+ mediump vec4 vertexPosition = vec4( floor( ( aPosition + anchorPoint ) * visualSize + ( visualOffset + origin ) * uSize.xy ), 0.0, 1.0 );\n
+ mediump vec4 nonAlignedVertex = uViewMatrix * uModelMatrix * vertexPosition;\n
+ mediump vec4 pixelAlignedVertex = vec4 ( floor( nonAlignedVertex.xyz ), 1.0 );\n
+ \n
+ gl_Position = uProjection * pixelAlignedVertex;\n
}\n
);
highp vec2 texCoord;\n
texCoord.y = vTexCoord.y;\n
texCoord.x = fract( vTexCoord.x ) / vRatio;\n
- if ( texCoord.x > 1.0 )\n
+ if ( texCoord.x > 1.0 || texCoord.y > 1.0 )\n
discard;\n
\n
gl_FragColor = texture2D( sTexture, texCoord );\n
);
/**
- * @brief How the text should be aligned when scrolling the text.
+ * @brief How the text should be aligned horizontally when scrolling the text.
*
- * 0.0f aligns the text to the left, 1.0f aligns the text to the right.
- * The final alignment depends on three factors:
+ * -0.5f aligns the text to the left, 0.0f aligns the text to the center, 0.5f aligns the text to the right.
+ * The final alignment depends on two factors:
* 1) The alignment value of the text label (Use Text::Layout::HorizontalAlignment enumerations).
* 2) The text direction, i.e. whether it's LTR or RTL (0 = LTR, 1 = RTL).
- * 3) Whether the text is greater than the size of the control ( 0 = Text width <= Control width, 1 = Text width > Control width ).
*/
-const float ALIGNMENT_TABLE[ Text::Layout::HORIZONTAL_ALIGN_COUNT ][ 2 ][ 2 ] =
+const float HORIZONTAL_ALIGNMENT_TABLE[ Text::Layout::HORIZONTAL_ALIGN_COUNT ][ 2 ] =
{
// HORIZONTAL_ALIGN_BEGIN
{
- { // LTR
- 0.0f, // Text width <= Control width
- 0.0f // Text width > Control width
- },
- { // RTL
- 1.0f, // Text width <= Control width
- 1.0f // Text width > Control width
- }
+ -0.5f, // LTR
+ 0.5f // RTL
},
// HORIZONTAL_ALIGN_CENTER
{
- { // LTR
- 0.5f, // Text width <= Control width
- 0.0f // Text width > Control width
- },
- { // RTL
- 0.5f, // Text width <= Control width
- 1.0f // Text width > Control width
- }
+ 0.0f, // LTR
+ 0.0f // RTL
},
// HORIZONTAL_ALIGN_END
{
- { // LTR
- 1.0f, // Text width <= Control width
- 0.0f // Text width > Control width
- },
- { // RTL
- 0.0f, // Text width <= Control width
- 1.0f // Text width > Control width
- }
+ 0.5f, // LTR
+ -0.5f // RTL
}
};
/**
- * @brief Create and set up a camera for the render task to use
- *
- * @param[in] sizeOfTarget size of the source camera to look at
- * @param[out] offscreenCamera custom camera
- */
-void CreateCameraActor( const Size& sizeOfTarget, CameraActor& offscreenCamera )
-{
- offscreenCamera = CameraActor::New();
- offscreenCamera.SetOrthographicProjection( sizeOfTarget );
- offscreenCamera.SetInvertYAxis( true );
-}
-
-/**
- * @brief Create a render task
+ * @brief How the text should be aligned vertically when scrolling the text.
*
- * @param[in] sourceActor actor to be used as source
- * @param[in] cameraActor camera looking at source
- * @param[in] offscreenTarget resulting image from render task
- * @param[out] renderTask render task that has been setup
+ * -0.5f aligns the text to the top, 0.0f aligns the text to the center, 0.5f aligns the text to the bottom.
+ * The alignment depends on the alignment value of the text label (Use Text::Layout::VerticalAlignment enumerations).
*/
-void CreateRenderTask( Actor sourceActor, CameraActor cameraActor , FrameBufferImage offscreenTarget, RenderTask& renderTask )
+const float VERTICAL_ALIGNMENT_TABLE[ Text::Layout::VERTICAL_ALIGN_COUNT ] =
{
- Stage stage = Stage::GetCurrent();
- RenderTaskList taskList = stage.GetRenderTaskList();
- renderTask = taskList.CreateTask();
- renderTask.SetSourceActor( sourceActor );
- renderTask.SetExclusive( true );
- renderTask.SetInputEnabled( false );
- renderTask.SetClearEnabled( true );
- renderTask.SetCameraActor( cameraActor );
- renderTask.SetTargetFrameBuffer( offscreenTarget );
- renderTask.SetClearColor( Color::TRANSPARENT );
- renderTask.SetCullMode( false );
-}
-
-/**
- * @brief Create quad geometry for the mesh
- *
- * @param[out] geometry quad geometry that can be used for a mesh
- */
-void CreateGeometry( Geometry& geometry )
-{
- struct QuadVertex { Vector2 position; };
-
- QuadVertex quadVertexData[4] =
- {
- { Vector2( 0.0f, 0.0f) },
- { Vector2( 1.0f, 0.0f) },
- { Vector2( 0.0f, 1.0f) },
- { Vector2( 1.0f, 1.0f) },
- };
-
- const unsigned short indices[6] =
- {
- 3,1,0,0,2,3
- };
-
- Property::Map quadVertexFormat;
- quadVertexFormat["aPosition"] = Property::VECTOR2;
- PropertyBuffer quadVertices = PropertyBuffer::New( quadVertexFormat );
- quadVertices.SetData(quadVertexData, 4 );
-
- geometry = Geometry::New();
- geometry.AddVertexBuffer( quadVertices );
- geometry.SetIndexBuffer( indices, sizeof(indices)/sizeof(indices[0]) );
-}
-
-
-/**
- * @brief Create a renderer
- *
- * @param[in] frameBufferImage texture to be used
- * @param[out] renderer mesh renderer using the supplied texture
- */
-void CreateRenderer( FrameBufferImage frameBufferImage, Dali::Renderer& renderer )
-{
- Shader shader = Shader::New( VERTEX_SHADER_SCROLL , FRAGMENT_SHADER, Shader::Hint::NONE );
-
- Sampler sampler = Sampler::New();
- sampler.SetFilterMode(FilterMode::LINEAR, FilterMode::LINEAR );
-
- TextureSet textureSet = TextureSet::New();
- TextureSetImage( textureSet, 0u, frameBufferImage );
- textureSet.SetSampler( 0u, sampler );
-
- Geometry meshGeometry;
- CreateGeometry( meshGeometry );
-
- renderer = Renderer::New( meshGeometry, shader );
- renderer.SetProperty( Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true );
- renderer.SetTextures( textureSet );
-}
+ -0.5f, // VERTICAL_ALIGN_TOP
+ 0.0f, // VERTICAL_ALIGN_CENTER
+ 0.5f // VERTICAL_ALIGN_BOTTOM
+};
} // namespace
case DevelTextLabel::AutoScrollStopMode::IMMEDIATE:
{
mScrollAnimation.Stop();
- CleanUp();
mScrollerInterface.ScrollingFinished();
break;
}
return mStopMode;
}
-Actor TextScroller::GetSourceCamera() const
-{
- return mOffscreenCameraActor;
-}
-
-Actor TextScroller::GetScrollingText() const
-{
- return mScrollingTextActor;
-}
-
-TextScroller::TextScroller( ScrollerInterface& scrollerInterface ) : mScrollerInterface( scrollerInterface ),
- mScrollDeltaIndex( Property::INVALID_INDEX ),
- mScrollSpeed( MINIMUM_SCROLL_SPEED ),
- mLoopCount( 1 ),
- mLoopDelay( 0.0f ),
- mWrapGap( 0.0f ),
- mStopMode( DevelTextLabel::AutoScrollStopMode::FINISH_LOOP )
+TextScroller::TextScroller( ScrollerInterface& scrollerInterface )
+: mScrollerInterface( scrollerInterface ),
+ mScrollDeltaIndex( Property::INVALID_INDEX ),
+ mScrollSpeed( MINIMUM_SCROLL_SPEED ),
+ mLoopCount( 1 ),
+ mLoopDelay( 0.0f ),
+ mWrapGap( 0.0f ),
+ mStopMode( DevelTextLabel::AutoScrollStopMode::FINISH_LOOP )
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller Default Constructor\n" );
}
TextScroller::~TextScroller()
{
- CleanUp();
}
-void TextScroller::SetParameters( Actor sourceActor, const Size& controlSize, const Size& offScreenSize, CharacterDirection direction, float alignmentOffset, Layout::HorizontalAlignment horizontalAlignment )
+void TextScroller::SetParameters( Actor scrollingTextActor, Renderer renderer, TextureSet textureSet, const Size& controlSize, const Size& textNaturalSize, CharacterDirection direction, Layout::HorizontalAlignment horizontalAlignment, Layout::VerticalAlignment verticalAlignment )
{
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::SetParameters controlSize[%f,%f] offscreenSize[%f,%f] direction[%d] alignmentOffset[%f]\n",
- controlSize.x, controlSize.y, offScreenSize.x, offScreenSize.y, direction, alignmentOffset );
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::SetParameters controlSize[%f,%f] offscreenSize[%f,%f] direction[%d]\n",
+ controlSize.x, controlSize.y, textNaturalSize.x, textNaturalSize.y, direction );
- CleanUp(); // If already scrolling then restart with new parameters
+ mRenderer = renderer;
float animationProgress = 0.0f;
int remainedLoop = mLoopCount;
}
}
mScrollAnimation.Clear();
- }
-
- FrameBufferImage offscreenRenderTargetForText = FrameBufferImage::New( offScreenSize.width, offScreenSize.height, Pixel::RGBA8888 );
- Renderer renderer;
- CreateCameraActor( offScreenSize, mOffscreenCameraActor );
- CreateRenderer( offscreenRenderTargetForText, renderer );
- CreateRenderTask( sourceActor, mOffscreenCameraActor, offscreenRenderTargetForText, mRenderTask );
-
- float xPosition = 0.0f;
- switch( horizontalAlignment )
- {
- case Layout::HORIZONTAL_ALIGN_BEGIN:
- {
- // Reposition camera to match alignment of target, RTL text has direction=true
- if ( direction )
- {
- xPosition = alignmentOffset + offScreenSize.width * 0.5f;
- }
- else
- {
- xPosition = offScreenSize.width * 0.5f;
- }
- break;
- }
-
- case Layout::HORIZONTAL_ALIGN_CENTER:
- {
- xPosition = controlSize.width * 0.5f;
- break;
- }
-
- case Layout::HORIZONTAL_ALIGN_END:
- {
- // Reposition camera to match alignment of target, RTL text has direction=true
- if ( direction )
- {
- xPosition = offScreenSize.width * 0.5f;
- }
- else
- {
- xPosition = alignmentOffset + offScreenSize.width * 0.5f;
- }
- break;
- }
+ // Reset to the original shader and texture before scrolling
+ mRenderer.SetShader(mShader);
+ mRenderer.SetTextures( mTextureSet );
}
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::SetParameters xPosition[%f]\n", xPosition );
+ mShader = mRenderer.GetShader();
+ mTextureSet = mRenderer.GetTextures();
- mOffscreenCameraActor.SetX( xPosition );
- mOffscreenCameraActor.SetY( offScreenSize.height * 0.5f );
+ // Set the shader and texture for scrolling
+ Shader shader = Shader::New( VERTEX_SHADER_SCROLL, FRAGMENT_SHADER, Shader::Hint::NONE );
+ mRenderer.SetShader( shader );
+ mRenderer.SetTextures( textureSet );
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::SetParameters mWrapGap[%f]\n", mWrapGap );
- const float align = ALIGNMENT_TABLE[ horizontalAlignment ][ direction ][ offScreenSize.width > controlSize.width ];
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::SetParameters align[%f]\n", align );
+ const float horizontalAlign = HORIZONTAL_ALIGNMENT_TABLE[ horizontalAlignment ][ direction ];
+ const float verticalAlign = VERTICAL_ALIGNMENT_TABLE[ verticalAlignment ];
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::SetParameters horizontalAlign[%f], verticalAlign[%f]\n", horizontalAlign, verticalAlign );
- mScrollingTextActor = Actor::New();
- mScrollingTextActor.AddRenderer( renderer );
- mScrollingTextActor.RegisterProperty( "uTextureSize", offScreenSize );
- mScrollingTextActor.RegisterProperty( "uAlign", align );
- mScrollingTextActor.RegisterProperty( "uGap", mWrapGap );
- mScrollingTextActor.SetSize( controlSize.width, std::min( offScreenSize.height, controlSize.height ) );
- mScrollDeltaIndex = mScrollingTextActor.RegisterProperty( "uDelta", 0.0f );
+ scrollingTextActor.RegisterProperty( "uTextureSize", textNaturalSize );
+ scrollingTextActor.RegisterProperty( "uHorizontalAlign", horizontalAlign );
+ scrollingTextActor.RegisterProperty( "uVerticalAlign", verticalAlign );
+ scrollingTextActor.RegisterProperty( "uGap", mWrapGap );
+ mScrollDeltaIndex = scrollingTextActor.RegisterProperty( "uDelta", 0.0f );
- float scrollAmount = std::max( offScreenSize.width + mWrapGap, controlSize.width );
+ float scrollAmount = std::max( textNaturalSize.width + mWrapGap, controlSize.width );
float scrollDuration = scrollAmount / mScrollSpeed;
if ( direction )
{
- scrollAmount = -scrollAmount; // reverse direction of scrollung
+ scrollAmount = -scrollAmount; // reverse direction of scrolling
}
- StartScrolling( scrollAmount, scrollDuration, remainedLoop );
+ StartScrolling( scrollingTextActor, scrollAmount, scrollDuration, remainedLoop );
mScrollAnimation.SetCurrentProgress(animationProgress);
}
void TextScroller::AutoScrollAnimationFinished( Dali::Animation& animation )
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::AutoScrollAnimationFinished\n" );
- CleanUp();
mScrollerInterface.ScrollingFinished();
+
+ // Revert to the original shader and texture after scrolling
+ mRenderer.SetShader(mShader);
+ if ( mTextureSet )
+ {
+ mRenderer.SetTextures( mTextureSet );
+ }
}
-void TextScroller::StartScrolling( float scrollAmount, float scrollDuration, int loopCount )
+void TextScroller::StartScrolling( Actor scrollingTextActor, float scrollAmount, float scrollDuration, int loopCount )
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::StartScrolling scrollAmount[%f] scrollDuration[%f], loop[%d] speed[%d]\n", scrollAmount, scrollDuration, loopCount, mScrollSpeed );
mScrollAnimation = Animation::New( scrollDuration );
- mScrollAnimation.AnimateTo( Property( mScrollingTextActor, mScrollDeltaIndex ), scrollAmount, TimePeriod( mLoopDelay, scrollDuration ) );
+ mScrollAnimation.AnimateTo( Property( scrollingTextActor, mScrollDeltaIndex ), scrollAmount, TimePeriod( mLoopDelay, scrollDuration ) );
mScrollAnimation.SetEndAction( Animation::Discard );
mScrollAnimation.SetLoopCount( loopCount );
mScrollAnimation.FinishedSignal().Connect( this, &TextScroller::AutoScrollAnimationFinished );
mScrollAnimation.Play();
}
-void TextScroller::CleanUp()
-{
- if ( Stage::IsInstalled() )
- {
- Stage stage = Stage::GetCurrent();
- RenderTaskList taskList = stage.GetRenderTaskList();
- UnparentAndReset( mScrollingTextActor );
- UnparentAndReset( mOffscreenCameraActor );
- taskList.RemoveTask( mRenderTask );
- }
-}
-
} // namespace Text
} // namespace Toolkit
#include <dali/public-api/actors/camera-actor.h>
#include <dali/public-api/animation/animation.h>
#include <dali/public-api/render-tasks/render-task.h>
+#include <dali/public-api/rendering/renderer.h>
// INTERNAL INCLUDES
#include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
/**
* @brief Set parameters relating to source required for scrolling
*
- * @param[in] sourceActor source actor to be scrolled
+ * @param[in] scrollingTextActor actor containing the text to be scrolled
+ * @param[in] renderer renderer to render the text
+ * @param[in] textureSet texture of the text to be scrolled
* @param[in] controlSize size of the control to scroll within
- * @param[in] offScreenSize size of the sourceActor
+ * @param[in] textNaturalSize natural size of the text
* @param[in] direction text direction true for right to left text
- * @param[in] alignmentOffset alignment of source text
- *
+ * @param[in] horizontalAlignment horizontal alignment of the text
+ * @param[in] verticalAlignment vertical alignment of the text
*/
- void SetParameters( Actor sourceActor, const Size& controlSize, const Size& offScreenSize, CharacterDirection direction, float alignmentOffset, Layout::HorizontalAlignment horizontalAlignment );
+ void SetParameters( Actor scrollingTextActor, Dali::Renderer renderer, TextureSet textureSet, const Size& controlSize, const Size& offScreenSize, CharacterDirection direction, Layout::HorizontalAlignment horizontalAlignment, Layout::VerticalAlignment verticalAlignment );
/**
* @brief Set the gap distance to elapse before the text wraps around
*/
DevelTextLabel::AutoScrollStopMode::Type GetStopMode() const;
- /**
- * @brief Get the camera used to look at source, should be added to the parent of target actor.
- * @return camera Actor
- */
- Actor GetSourceCamera() const;
-
- /**
- * @brief Get the resulting scrolling text actor, add to target actor which will show scrolling text
- * @return mesh Actor
- */
- Actor GetScrollingText() const;
-
private: // Implementation
/**
/**
* @brief variables required to set up scrolling animation
+ * @param[in] scrollingTextActor actor that shows scrolling text
* @param[in] scrollAmount distance to animate text for the given duration
* @param[in] scrollDuration duration of aninmation
* @param[in] loopCount number of times to loop the scrolling text
*/
- void StartScrolling( float scrollAmount, float scrollDuration, int loopCount );
-
- /**
- * @brief When scrolling ended, the actors are cleaned up so no longer staged.
- */
- void CleanUp();
+ void StartScrolling( Actor scrollingTextActor, float scrollAmount, float scrollDuration, int loopCount );
private:
- RenderTask mRenderTask; // Renders full text to a FrameBuffer which is then scrolled.
- CameraActor mOffscreenCameraActor; // Camera used by render task
- Actor mScrollingTextActor; // Actor used to show scrolling text
ScrollerInterface& mScrollerInterface; // Interface implemented by control that requires scrolling
Property::Index mScrollDeltaIndex; // Property used by shader to represent distance to scroll
Animation mScrollAnimation; // Animation used to update the mScrollDeltaIndex
+ Dali::Renderer mRenderer; // Renderer used to render the text
+ Shader mShader; // Shader originally used by the renderer while not scrolling
+ TextureSet mTextureSet; // Texture originally used by the renderer while not scrolling
int mScrollSpeed; ///< Speed which text should automatically scroll at
int mLoopCount; ///< Number of time the text should scroll
{
if( mImpl->mVisualModel )
{
- return mImpl->mVisualModel->mUnderlineRuns.Count();
+ return mImpl->mVisualModel->GetNumberOfUnderlineRuns();
}
return 0u;
return mUnderlineHeight;
}
+Length VisualModel::GetNumberOfUnderlineRuns() const
+{
+ return mUnderlineRuns.Count();
+}
+
void VisualModel::ClearCaches()
{
mCachedLineIndex = 0u;
*/
float GetUnderlineHeight() const;
+ /**
+ * @brief Retrieves the number of underline runs.
+ *
+ * @return The number of underline runs.
+ */
+ Length GetNumberOfUnderlineRuns() const;
+
protected:
/**
// CLASS HEADER
#include <dali-toolkit/internal/visuals/text/text-visual.h>
+// EXTERNAL INCLUDES
+#include <dali/public-api/animation/constraints.h>
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+
// INTERNAL HEADER
#include <dali-toolkit/devel-api/visuals/text-visual-properties.h>
#include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
#include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
#include <dali-toolkit/internal/visuals/visual-string-constants.h>
#include <dali-toolkit/internal/text/text-font-style.h>
+#include <dali-toolkit/internal/text/text-effects-style.h>
+#include <dali-toolkit/internal/text/script-run.h>
namespace Dali
{
const char * const VERTICAL_ALIGNMENT_PROPERTY( "verticalAlignment" );
const char * const TEXT_COLOR_PROPERTY( "textColor" );
const char * const ENABLE_MARKUP_PROPERTY( "enableMarkup" );
+const char * const SHADOW_PROPERTY( "shadow" );
+const char * const UNDERLINE_PROPERTY( "underline" );
const Scripting::StringEnum HORIZONTAL_ALIGNMENT_STRING_TABLE[] =
{
attribute mediump vec2 aPosition;\n
uniform mediump mat4 uMvpMatrix;\n
uniform mediump vec3 uSize;\n
- uniform mediump vec4 pixelArea;
+ uniform mediump vec4 pixelArea;\n
uniform mediump mat4 uModelMatrix;\n
uniform mediump mat4 uViewMatrix;\n
uniform mediump mat4 uProjection;\n
varying mediump vec2 vTexCoord;\n
- \n
+
//Visual size and offset
uniform mediump vec2 offset;\n
uniform mediump vec2 size;\n
}\n
);
-const char* FRAGMENT_SHADER_ATLAS_CLAMP = DALI_COMPOSE_SHADER(
- varying mediump vec2 vTexCoord;\n
- uniform sampler2D sTexture;\n
- uniform mediump vec4 uAtlasRect;\n
- uniform lowp vec4 uColor;\n
- uniform lowp vec3 mixColor;\n
- uniform lowp float opacity;\n
- \n
- void main()\n
- {\n
- mediump vec2 texCoord = clamp( mix( uAtlasRect.xy, uAtlasRect.zw, vTexCoord ), uAtlasRect.xy, uAtlasRect.zw );\n
- gl_FragColor = texture2D( sTexture, texCoord ) * uColor * vec4( mixColor, opacity );\n
- }\n
+const char* FRAGMENT_SHADER_ATLAS_CLAMP_RGBA = DALI_COMPOSE_SHADER(
+ varying mediump vec2 vTexCoord;\n
+ uniform sampler2D sTexture;\n
+ uniform sampler2D sStyle;\n
+ uniform sampler2D sMask;\n
+ uniform lowp float uHasMultipleTextColors;\n
+ uniform lowp vec4 uTextColorAnimatable;\n
+ uniform mediump vec4 uAtlasRect;\n
+ uniform lowp vec4 uColor;\n
+ uniform lowp vec3 mixColor;\n
+ uniform lowp float opacity;\n
+ \n
+ void main()\n
+ {\n
+ mediump vec2 texCoord = clamp( mix( uAtlasRect.xy, uAtlasRect.zw, vTexCoord ), uAtlasRect.xy, uAtlasRect.zw );\n
+ mediump vec4 textTexture = texture2D( sTexture, texCoord );\n
+ mediump vec4 styleTexture = texture2D( sStyle, texCoord );\n
+ mediump vec4 maskTexture = texture2D( sMask, texCoord );\n
+
+ // Set the color of non-transparent pixel in text to what it is animated to.
+ // Markup text with multiple text colors are not animated (but can be supported later on if required).
+ // Emoji color are not animated.
+ mediump vec4 textColor = textTexture * textTexture.a;\n
+ mediump float vstep = step( 0.0001, textColor.a );\n
+ textColor.rgb = mix( textColor.rgb, uTextColorAnimatable.rgb, vstep * maskTexture.a * ( 1.0 - uHasMultipleTextColors ) );\n
+
+ // Draw the text as overlay above the style
+ gl_FragColor = ( textColor + styleTexture * ( 1.0 - textTexture.a ) ) * uColor * vec4( mixColor, opacity );\n
+ }\n
+);
+
+const char* FRAGMENT_SHADER_ATLAS_CLAMP_L8 = DALI_COMPOSE_SHADER(
+ varying mediump vec2 vTexCoord;\n
+ uniform sampler2D sTexture;\n
+ uniform lowp vec4 uTextColorAnimatable;\n
+ uniform mediump vec4 uAtlasRect;\n
+ uniform lowp vec4 uColor;\n
+ uniform lowp vec3 mixColor;\n
+ uniform lowp float opacity;\n
+ \n
+ void main()\n
+ {\n
+ mediump vec2 texCoord = clamp( mix( uAtlasRect.xy, uAtlasRect.zw, vTexCoord ), uAtlasRect.xy, uAtlasRect.zw );\n
+ mediump float textTexture = texture2D( sTexture, texCoord ).r;\n
+
+ // Set the color of the text to what it is animated to.
+ gl_FragColor = uTextColorAnimatable * textTexture * uColor * vec4( mixColor, opacity );\n
+ }\n
);
/**
{
result = Toolkit::TextVisual::Property::ENABLE_MARKUP;
}
+ else if( stringKey == SHADOW_PROPERTY )
+ {
+ result = Toolkit::TextVisual::Property::SHADOW;
+ }
+ else if( stringKey == UNDERLINE_PROPERTY )
+ {
+ result = Toolkit::TextVisual::Property::UNDERLINE;
+ }
return result;
}
map.Insert( Toolkit::TextVisual::Property::TEXT_COLOR, mController->GetDefaultColor() );
map.Insert( Toolkit::TextVisual::Property::ENABLE_MARKUP, mController->IsMarkupProcessorEnabled() );
+
+ GetShadowProperties( mController, value, Text::EffectStyle::DEFAULT );
+ map.Insert( Toolkit::TextVisual::Property::SHADOW, value );
+
+ GetUnderlineProperties( mController, value, Text::EffectStyle::DEFAULT );
+ map.Insert( Toolkit::TextVisual::Property::UNDERLINE, value );
}
void TextVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
TextVisual::TextVisual( VisualFactoryCache& factoryCache )
: Visual::Base( factoryCache ),
mController( Text::Controller::New() ),
- mTypesetter( Text::Typesetter::New( mController->GetTextModel() ) )
+ mTypesetter( Text::Typesetter::New( mController->GetTextModel() ) ),
+ mAnimatableTextColorPropertyIndex( Property::INVALID_INDEX ),
+ mRendererUpdateNeeded( false )
{
}
mControl = actor;
Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
+ Shader shader = GetTextShader(mFactoryCache, true);
- Shader shader = mFactoryCache.GetShader( VisualFactoryCache::TEXT_SHADER );
- if( ! shader )
- {
- shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_CLAMP );
- shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
+ mImpl->mRenderer = Renderer::New( geometry, shader );
+ mImpl->mRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT );
- mFactoryCache.SaveShader( VisualFactoryCache::TEXT_SHADER, shader );
+ const Vector4& defaultColor = mController->GetTextModel()->GetDefaultColor();
+ Dali::Property::Index shaderTextColorIndex = mImpl->mRenderer.RegisterProperty( "uTextColorAnimatable", defaultColor );
+
+ if ( mAnimatableTextColorPropertyIndex != Property::INVALID_INDEX )
+ {
+ // Create constraint for the animatable text's color Property with uTextColorAnimatable in the renderer.
+ if( shaderTextColorIndex != Property::INVALID_INDEX )
+ {
+ Constraint constraint = Constraint::New<Vector4>( mImpl->mRenderer, shaderTextColorIndex, EqualToConstraint() );
+ constraint.AddSource( Source( actor, mAnimatableTextColorPropertyIndex ) );
+ constraint.Apply();
+ }
}
- mImpl->mRenderer = Renderer::New( geometry, shader );
- mImpl->mRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT );
+ // Renderer needs textures and to be added to control
+ mRendererUpdateNeeded = true;
- UpdateRenderer( true ); // Renderer needs textures and to be added to control
+ UpdateRenderer();
}
void TextVisual::DoSetOffStage( Actor& actor )
void TextVisual::OnSetTransform()
{
- UpdateRenderer( false );
+ UpdateRenderer();
}
void TextVisual::DoSetProperty( Dali::Property::Index index, const Dali::Property::Value& propertyValue )
}
break;
}
+ case Toolkit::TextVisual::Property::SHADOW:
+ {
+ SetShadowProperties( mController, propertyValue, Text::EffectStyle::DEFAULT );
+ break;
+ }
+ case Toolkit::TextVisual::Property::UNDERLINE:
+ {
+ SetUnderlineProperties( mController, propertyValue, Text::EffectStyle::DEFAULT );
+ break;
+ }
}
}
-void TextVisual::UpdateRenderer( bool initializeRendererAndTexture )
+void TextVisual::UpdateRenderer()
{
Actor control = mControl.GetHandle();
if( !control )
const Text::Controller::UpdateTextType updateTextType = mController->Relayout( relayoutSize );
- if( Text::Controller::NONE_UPDATED != ( Text::Controller::MODEL_UPDATED & updateTextType ) || initializeRendererAndTexture )
+ if( Text::Controller::NONE_UPDATED != ( Text::Controller::MODEL_UPDATED & updateTextType )
+ || mRendererUpdateNeeded )
{
+ mRendererUpdateNeeded = false;
+
// Removes the texture set.
RemoveTextureSet();
if( ( relayoutSize.width > Math::MACHINE_EPSILON_1000 ) &&
( relayoutSize.height > Math::MACHINE_EPSILON_1000 ) )
{
- PixelData data = mTypesetter->Render( relayoutSize );
+ // Check whether it is a markup text with multiple text colors
+ const Vector4* const colorsBuffer = mController->GetTextModel()->GetColors();
+ bool hasMultipleTextColors = ( NULL != colorsBuffer );
- Vector4 atlasRect = FULL_TEXTURE_RECT;
+ // Check whether the text contains any emoji
+ bool containsEmoji = false;
+
+ Text::ScriptRunIndex numberOfScripts = mController->GetTextModel()->GetNumberOfScripts();
+ const Text::ScriptRun* scripts = mController->GetTextModel()->GetScriptRuns();
+ for ( Text::ScriptRunIndex scriptIndex = 0u; scriptIndex < numberOfScripts; scriptIndex++ )
+ {
+ const Text::ScriptRun& scriptRun = *( scripts + scriptIndex );
+ if( TextAbstraction::EMOJI == scriptRun.script )
+ {
+ containsEmoji = true;
+ break;
+ }
+ }
+
+ // Check whether the text contains any style colors (e.g. underline color, shadow color, etc.)
+ bool shadowEnabled = false;
+ const Vector2& shadowOffset = mController->GetTextModel()->GetShadowOffset();
+ if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 )
+ {
+ shadowEnabled = true;
+ }
+
+ const bool underlineEnabled = mController->GetTextModel()->IsUnderlineEnabled();
+
+ if ( hasMultipleTextColors || containsEmoji || shadowEnabled || underlineEnabled )
+ {
+ // Create RGBA textures if the text contains emojis or styles or multiple text colors
+
+ // Create a texture for the text without any styles
+ PixelData data = mTypesetter->Render( relayoutSize, Text::Typesetter::RENDER_NO_STYLES );
+
+ // 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.
+
+ Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D,
+ data.GetPixelFormat(),
+ data.GetWidth(),
+ data.GetHeight() );
+
+ texture.Upload( data );
+
+ TextureSet textureSet = TextureSet::New();
+ textureSet.SetTexture( 0u, texture );
+
+ // Create a texture for all the text styles (without the text itself)
+ PixelData styleData = mTypesetter->Render( relayoutSize, Text::Typesetter::RENDER_NO_TEXT );
- // Texture set not retrieved from Atlas Manager whilst pixel offset visible.
+ Texture styleTexture = Texture::New( Dali::TextureType::TEXTURE_2D,
+ styleData.GetPixelFormat(),
+ styleData.GetWidth(),
+ styleData.GetHeight() );
+
+ styleTexture.Upload( styleData );
+
+ textureSet.SetTexture( 1u, styleTexture );
+
+ // Create a texture as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
+ PixelData maskData = mTypesetter->Render( relayoutSize, Text::Typesetter::RENDER_MASK );
+
+ Texture maskTexture = Texture::New( Dali::TextureType::TEXTURE_2D,
+ styleData.GetPixelFormat(),
+ styleData.GetWidth(),
+ styleData.GetHeight() );
+
+ maskTexture.Upload( maskData );
+
+ textureSet.SetTexture( 2u, maskTexture );
+
+ // Filter mode needs to be set to nearest to produce better quality while static.
+ Sampler sampler = Sampler::New();
+ sampler.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR );
+ textureSet.SetSampler( 0u, sampler );
+ textureSet.SetSampler( 1u, sampler );
+ textureSet.SetSampler( 2u, sampler );
+
+ mImpl->mRenderer.SetTextures( textureSet );
+
+ Shader shader = GetTextShader(mFactoryCache, true); // RGBA shader
+ mImpl->mRenderer.SetShader(shader);
+ }
+ else
+ {
+ // Create L8 texture if the text contains only single text color with no emoji and no style
- // 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.
+ // Create a texture for the text without any styles
+ PixelData data = mTypesetter->Render( relayoutSize, Text::Typesetter::RENDER_NO_STYLES, false, Pixel::L8 );
- Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D,
- data.GetPixelFormat(),
- data.GetWidth(),
- data.GetHeight() );
+ // 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.
- texture.Upload( data );
+ Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D,
+ data.GetPixelFormat(),
+ data.GetWidth(),
+ data.GetHeight() );
- TextureSet textureSet = TextureSet::New();
- textureSet.SetTexture( 0u, texture );
+ texture.Upload( data );
+
+ TextureSet textureSet = TextureSet::New();
+ textureSet.SetTexture( 0u, texture );
+
+ // Filter mode needs to be set to nearest to produce better quality while static.
+ Sampler sampler = Sampler::New();
+ sampler.SetFilterMode( FilterMode::NEAREST, FilterMode::NEAREST );
+ textureSet.SetSampler( 0u, sampler );
+
+ mImpl->mRenderer.SetTextures( textureSet );
+
+ Shader shader = GetTextShader(mFactoryCache, false); // L8 shader
+ mImpl->mRenderer.SetShader(shader);
+ }
mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
+ Vector4 atlasRect = FULL_TEXTURE_RECT;
mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
+ mImpl->mRenderer.RegisterProperty( "uHasMultipleTextColors", static_cast<float>( hasMultipleTextColors ) );
+
+ mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON);
//Register transform properties
mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
- // Filter mode needs to be set to nearest to avoid blurry text.
- Sampler sampler = Sampler::New();
- sampler.SetFilterMode( FilterMode::NEAREST, FilterMode::NEAREST );
- textureSet.SetSampler( 0u, sampler );
-
- mImpl->mRenderer.SetTextures( textureSet );
-
control.AddRenderer( mImpl->mRenderer );
// Text rendered and ready to display
}
}
+Shader TextVisual::GetTextShader( VisualFactoryCache& factoryCache, bool isRgbaTexture )
+{
+ Shader shader;
+ if( isRgbaTexture )
+ {
+ shader = factoryCache.GetShader( VisualFactoryCache::TEXT_SHADER_RGBA );
+ if( !shader )
+ {
+ shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_CLAMP_RGBA );
+ shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
+ factoryCache.SaveShader( VisualFactoryCache::TEXT_SHADER_RGBA, shader );
+ }
+ }
+ else
+ {
+ shader = factoryCache.GetShader( VisualFactoryCache::TEXT_SHADER_L8 );
+ if( !shader )
+ {
+ shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_CLAMP_L8 );
+ shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
+ factoryCache.SaveShader( VisualFactoryCache::TEXT_SHADER_L8, shader );
+ }
+ }
+
+ return shader;
+}
+
} // namespace Internal
} // namespace Toolkit
*
*/
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-object.h>
+
// INTERNAL INCLUDES
#include <dali-toolkit/internal/visuals/visual-base-impl.h>
#include <dali-toolkit/internal/text/rendering/text-typesetter.h>
*/
static void ConvertStringKeysToIndexKeys( Property::Map& propertyMap );
+ /**
+ * @brief Retrieve the text's controller.
+ * @param[in] visual The text visual.
+ * @return The text controller
+ */
+ static Text::ControllerPtr GetController( Toolkit::Visual::Base visual )
+ {
+ return GetVisualObject( visual ).mController;
+ };
+
+ /**
+ * @brief Set the index of the animatable text color property.
+ * @param[in] visual The text visual.
+ * @param[in] animatablePropertyIndex The index of the animatable property
+ */
+ static void SetAnimatableTextColorProperty( Toolkit::Visual::Base visual, Property::Index animatablePropertyIndex )
+ {
+ GetVisualObject( visual ).mAnimatableTextColorPropertyIndex = animatablePropertyIndex;
+ };
+
+ /**
+ * @brief Set the flag to trigger the textures to be initialized and renderer to be added to the control.
+ * @param[in] visual The text visual.
+ */
+ static void EnableRendererUpdate( Toolkit::Visual::Base visual )
+ {
+ GetVisualObject( visual ).mRendererUpdateNeeded = true;
+ };
+
public: // from Visual::Base
/**
/**
* @brief Updates the text's renderer.
- * @param[in] initializeRendererAndTexture Set flag to true to initialize textures and add renderer to control.
*/
- void UpdateRenderer( bool initializeRendererAndTexture );
+ void UpdateRenderer();
/**
* @brief Removes the texture set from the renderer.
*/
void RemoveTextureSet();
+ /**
+ * Get the text rendering shader.
+ * @param[in] factoryCache A pointer pointing to the VisualFactoryCache object
+ * @param[in] isRgbaTexture Whether the texture is in RGBA format.
+ */
+ Shader GetTextShader( VisualFactoryCache& factoryCache, bool isRgbaTexture );
+
+ /**
+ * @brief Retrieve the text's controller.
+ * @param[in] visual The text visual.
+ * @return The text controller
+ */
+ static TextVisual& GetVisualObject( Toolkit::Visual::Base visual )
+ {
+ return static_cast<TextVisual&>( visual.GetBaseObject() );
+ };
+
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.
+ 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.
};
} // namespace Internal
IMAGE_SHADER_ATLAS_CUSTOM_WRAP,
NINE_PATCH_SHADER,
SVG_SHADER,
- TEXT_SHADER,
+ TEXT_SHADER_RGBA,
+ TEXT_SHADER_L8,
WIREFRAME_SHADER,
SHADER_TYPE_MAX = WIREFRAME_SHADER
};
enum
{
/**
+ * DEPRECATED_1_2.53 No longer be supported and will be ignored.
* @brief The type of rendering e.g. bitmap-based.
* @details name "renderingBackend", type INT, default RENDERING_SHARED_ATLAS.
* @SINCE_1_0.0