Revert "Revert TextVisual in TextLabel Patches" 19/149619/3
authorRichard Huang <r.huang@samsung.com>
Tue, 12 Sep 2017 16:01:25 +0000 (16:01 +0000)
committerRichard Huang <r.huang@samsung.com>
Fri, 15 Sep 2017 10:02:51 +0000 (10:02 +0000)
This reverts commit 723acb540264b5f3bfc98ec3284891aa58d765c4.

Change-Id: I91b8dcb55fdb985e12ed5f3eea97f6bffccd938e

22 files changed:
automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp
dali-toolkit/devel-api/visuals/text-visual-properties.h
dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
dali-toolkit/internal/controls/text-controls/text-label-impl.h
dali-toolkit/internal/text/rendering/text-typesetter.cpp
dali-toolkit/internal/text/rendering/text-typesetter.h
dali-toolkit/internal/text/rendering/view-model.cpp
dali-toolkit/internal/text/rendering/view-model.h
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-controller.h
dali-toolkit/internal/text/text-model-interface.h
dali-toolkit/internal/text/text-model.cpp
dali-toolkit/internal/text/text-model.h
dali-toolkit/internal/text/text-scroller.cpp
dali-toolkit/internal/text/text-scroller.h
dali-toolkit/internal/text/text-view.cpp
dali-toolkit/internal/text/visual-model-impl.cpp
dali-toolkit/internal/text/visual-model-impl.h
dali-toolkit/internal/visuals/text/text-visual.cpp
dali-toolkit/internal/visuals/text/text-visual.h
dali-toolkit/internal/visuals/visual-factory-cache.h
dali-toolkit/public-api/controls/text-controls/text-label.h

index 1a642dde94140b0653543504fddcd38355421b99..beba3a6c1c873cb1005e8fc1fe3ee7e0ec3c168a 100644 (file)
@@ -986,6 +986,7 @@ int UtcDaliToolkitTextLabelColorComponents(void)
   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;
 }
index 10adcbcf2dc3ad603275f0cfaac3bff4cbc0510a..4286c385ea8e7c0c7e0dedcc30cc6a71c0df0555 100644 (file)
@@ -88,6 +88,18 @@ enum
    * @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
index db99cc47b4e114f5c9fa3cee8908e170870cb51d..e871db3d2b55f6e488e2d3b1699680eb8ecc73be 100644 (file)
 #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
@@ -97,7 +104,7 @@ BaseHandle Create()
 // 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             )
@@ -127,7 +134,7 @@ DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "autoScrollLoopDelay", FLO
 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)
@@ -162,6 +169,8 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
     {
       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
@@ -173,7 +182,7 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
         if( impl.mRenderingBackend != backend )
         {
           impl.mRenderingBackend = backend;
-          impl.mRenderer.Reset();
+          impl.mTextUpdateNeeded = true;
 
           if( impl.mController )
           {
@@ -262,7 +271,8 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
 
       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;
       }
 
@@ -274,7 +284,7 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
           if ( impl.mController->GetShadowOffset() != shadowOffset )
           {
             impl.mController->SetShadowOffset( shadowOffset );
-            impl.mRenderer.Reset();
+            impl.mTextUpdateNeeded = true;
           }
         }
         break;
@@ -287,7 +297,7 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
           if ( impl.mController->GetShadowColor() != shadowColor )
           {
             impl.mController->SetShadowColor( shadowColor );
-            impl.mRenderer.Reset();
+            impl.mTextUpdateNeeded = true;
           }
         }
         break;
@@ -300,7 +310,7 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
           if ( impl.mController->GetUnderlineColor() != color )
           {
             impl.mController->SetUnderlineColor( color );
-            impl.mRenderer.Reset();
+            impl.mTextUpdateNeeded = true;
           }
         }
         break;
@@ -313,7 +323,7 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
           if ( impl.mController->IsUnderlineEnabled() != enabled )
           {
             impl.mController->SetUnderlineEnabled( enabled );
-            impl.mRenderer.Reset();
+            impl.mTextUpdateNeeded = true;
           }
         }
         break;
@@ -327,7 +337,7 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
           if( fabsf( impl.mController->GetUnderlineHeight() - height ) > Math::MACHINE_EPSILON_1000 )
           {
             impl.mController->SetUnderlineHeight( height );
-            impl.mRenderer.Reset();
+            impl.mTextUpdateNeeded = true;
           }
         }
         break;
@@ -425,7 +435,7 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
         {
           const float lineSpacing = value.Get<float>();
           impl.mController->SetDefaultLineSpacing( lineSpacing );
-          impl.mRenderer.Reset();
+          impl.mTextUpdateNeeded = true;
         }
         break;
       }
@@ -434,7 +444,7 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
         const bool update = SetUnderlineProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
         if( update )
         {
-          impl.mRenderer.Reset();
+          impl.mTextUpdateNeeded = true;
         }
         break;
       }
@@ -443,7 +453,7 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
         const bool update = SetShadowProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
         if( update )
         {
-          impl.mRenderer.Reset();
+          impl.mTextUpdateNeeded = true;
         }
         break;
       }
@@ -452,7 +462,7 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
         const bool update = SetEmbossProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
         if( update )
         {
-          impl.mRenderer.Reset();
+          impl.mTextUpdateNeeded = true;
         }
         break;
       }
@@ -461,7 +471,7 @@ void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Pr
         const bool update = SetOutlineProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
         if( update )
         {
-          impl.mRenderer.Reset();
+          impl.mTextUpdateNeeded = true;
         }
         break;
       }
@@ -522,6 +532,8 @@ Property::Value TextLabel::GetProperty( BaseObject* object, Property::Index inde
     {
       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;
       }
@@ -594,10 +606,7 @@ Property::Value TextLabel::GetProperty( BaseObject* object, Property::Index inde
       }
       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:
@@ -783,11 +792,19 @@ void TextLabel::OnInitialize()
 {
   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 );
@@ -798,8 +815,6 @@ void TextLabel::OnInitialize()
 
   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 )
@@ -852,14 +867,13 @@ void TextLabel::OnPropertySet( Property::Index index, Property::Value propertyVa
 
   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;
     }
@@ -879,76 +893,50 @@ void TextLabel::OnRelayout( const Vector2& size, RelayoutContainer& container )
   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 )
   {
@@ -957,23 +945,27 @@ void TextLabel::SetUpAutoScrolling()
     // 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()
@@ -988,7 +980,7 @@ void TextLabel::ScrollingFinished()
 TextLabel::TextLabel()
 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
   mRenderingBackend( DEFAULT_RENDERING_BACKEND ),
-  mHasBeenStaged( false )
+  mTextUpdateNeeded( false )
 {
 }
 
index f15acbf70c5874f52e30dc18019e3f207e3c3ad5..85e94abcb22a459ce8083d46888e03fc81b99ea1 100644 (file)
@@ -18,6 +18,9 @@
  *
  */
 
+// 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>
@@ -26,6 +29,8 @@
 #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
 {
@@ -132,14 +137,6 @@ private:
   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
    */
@@ -148,11 +145,12 @@ private:
 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
index 78aba4930a775bf6b4ca7f92afc1ba5ac37ffc12..66bb797058df0432dfce0a5177bfc6e82c6fe966 100644 (file)
@@ -21,6 +21,7 @@
 // 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>
@@ -42,7 +43,7 @@ namespace
  */
 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.
@@ -57,10 +58,14 @@ struct GlyphData
  * @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 ) )
   {
@@ -71,71 +76,179 @@ void TypesetGlyph( const GlyphData& data,
   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*>( &currentColor );
+
+            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 )
@@ -148,7 +261,7 @@ ViewModel* Typesetter::GetViewModel()
   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.
 
@@ -180,6 +293,73 @@ PixelData Typesetter::Render( const Vector2& size )
     }
   }
 
+  // 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();
@@ -190,26 +370,26 @@ PixelData Typesetter::Render( const Vector2& size )
   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();
@@ -220,15 +400,54 @@ PixelData Typesetter::Render( const Vector2& size )
     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;
 
@@ -239,12 +458,84 @@ PixelData Typesetter::Render( const Vector2& size )
         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;
@@ -259,11 +550,49 @@ PixelData Typesetter::Render( const Vector2& size )
       {
         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;
+        }
       }
     }
 
@@ -271,7 +600,70 @@ PixelData Typesetter::Render( const Vector2& size )
     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 )
index a5aab65dd4016ae9f805ff6d85e3371cd3de2fa4..285348e4f9ad5a7a0a539c5506576340ba6446dd 100644 (file)
 // 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
 {
@@ -43,6 +46,33 @@ typedef IntrusivePtr<Typesetter> TypesetterPtr;
  */
 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.
@@ -68,15 +98,17 @@ public:
    * 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:
   /**
@@ -92,6 +124,46 @@ 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:
 
   /**
@@ -102,6 +174,7 @@ protected:
   virtual ~Typesetter();
 
 private:
+
    ViewModel* mModel;
 };
 
index eb49465bd9dac714d0b2ba43eab8bce110d0fe38..1dcda368b49fa7a008d9a3b0e136a26506ccb299 100644 (file)
@@ -86,6 +86,16 @@ const LineRun* const ViewModel::GetLines() const
   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() )
@@ -143,6 +153,41 @@ const Vector4& ViewModel::GetDefaultColor() const
   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;
index d178577bbfa23955c47e71c5e8a043d3f7f3cfd7..e15fa9c94a5aea245228371078babf0620f1b834 100644 (file)
@@ -96,6 +96,16 @@ public:
    */
   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()
    */
@@ -126,6 +136,41 @@ public:
    */
   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.
    *
index 7875e5c6ae97d2713a759463595d5fedb615bac5..258fea7b00edf555e1b9c7465a31d5f053b02de0 100644 (file)
@@ -1757,14 +1757,14 @@ Vector3 Controller::GetNaturalSize()
     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.
@@ -3694,6 +3694,11 @@ void Controller::ResetScrollPosition()
   }
 }
 
+void Controller::SetControlInterface( ControlInterface* controlInterface )
+{
+  mImpl->mControlInterface = controlInterface;
+}
+
 bool Controller::ShouldClearFocusOnEscape() const
 {
   return mImpl->mShouldClearFocusOnEscape;
index 1f64c246eec264f30f3933fdcbeec54e747fcaca..9e193c7c09435c75584e7e987ca4d65f2d29afe9 100644 (file)
@@ -999,6 +999,13 @@ public: // Default style & Input style
    */
   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.
 
   /**
index b3eb694d7c16b85f4459591c8ec22c72c312976a..38e1fa29ab163aa3ad87ba9bad215199bfe74e2d 100644 (file)
@@ -24,6 +24,7 @@
 // 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
@@ -103,6 +104,20 @@ public:
    */
   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.
    *
@@ -144,6 +159,57 @@ public:
    * @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
index 95f951433d37952116f72f63b8dfbb43752a66ad..2c00cf8be89e757445f9508f9b877a9617e444e2 100644 (file)
@@ -72,6 +72,16 @@ const LineRun* const Model::GetLines() const
   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();
@@ -102,6 +112,41 @@ const Vector4& Model::GetDefaultColor() const
   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(),
index 4256421efbd4c965790334408073c8cf63d85186..a7b0bb8adc6a5429e459a5b9578b666cedf34e90 100644 (file)
@@ -99,6 +99,16 @@ public:
    */
   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()
    */
@@ -129,6 +139,41 @@ public:
    */
   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.
 
   /**
index 590f919a6d4b1fdc77cbbe3f3e95938e1f22298a..b1096617cacd2e911e52dfefe5ccdf9ecd1c8aeb 100644 (file)
 
 // 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
@@ -51,30 +44,41 @@ const char* VERTEX_SHADER_SCROLL = DALI_COMPOSE_SHADER(
   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
 );
 
@@ -88,7 +92,7 @@ const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
     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
@@ -96,146 +100,46 @@ const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
 );
 
 /**
- * @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
 
@@ -311,7 +215,6 @@ void TextScroller::StopScrolling()
       case DevelTextLabel::AutoScrollStopMode::IMMEDIATE:
       {
         mScrollAnimation.Stop();
-        CleanUp();
         mScrollerInterface.ScrollingFinished();
         break;
       }
@@ -333,38 +236,28 @@ DevelTextLabel::AutoScrollStopMode::Type TextScroller::GetStopMode() const
   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;
@@ -381,114 +274,69 @@ void TextScroller::SetParameters( Actor sourceActor, const Size& controlSize, co
       }
     }
     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
index 2d611bbd8e6257fd45b40f886931a7aa602ff8b1..b8dc10f6d5323f441cac49f0b4c4b99e477c0a17 100644 (file)
@@ -22,6 +22,7 @@
 #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>
@@ -60,14 +61,16 @@ public:
   /**
    * @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
@@ -134,18 +137,6 @@ public:
    */
   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
 
   /**
@@ -172,25 +163,21 @@ 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
index 754b06d1645c794f374277db0830748884c0ba26..9e8d0bee9d71bba392a737965bfe2b6dd216038c 100644 (file)
@@ -384,7 +384,7 @@ Length View::GetNumberOfUnderlineRuns() const
 {
   if( mImpl->mVisualModel )
   {
-    return mImpl->mVisualModel->mUnderlineRuns.Count();
+    return mImpl->mVisualModel->GetNumberOfUnderlineRuns();
   }
 
   return 0u;
index 2ee0bab7ba00ae1c0d3afb49823a23b7d6fcb967..1b114c444a9d9bffae87eb138d19206eb66666b3 100644 (file)
@@ -390,6 +390,11 @@ float VisualModel::GetUnderlineHeight() const
   return mUnderlineHeight;
 }
 
+Length VisualModel::GetNumberOfUnderlineRuns() const
+{
+  return mUnderlineRuns.Count();
+}
+
 void VisualModel::ClearCaches()
 {
   mCachedLineIndex = 0u;
index 0874c0528183a6b92121db45f77725a288902f37..6356f32de5d05410f98a3b157d6fc8695581e633 100644 (file)
@@ -280,6 +280,13 @@ public:
    */
   float GetUnderlineHeight() const;
 
+  /**
+   * @brief Retrieves the number of underline runs.
+   *
+   * @return The number of underline runs.
+   */
+  Length GetNumberOfUnderlineRuns() const;
+
 protected:
 
   /**
index 889b57dcf2a0cfe4d050f222e462ed3f8a4abbea..c90c7274e79f91ed94a9f632b60a3d5415be75f3 100644 (file)
 // 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>
@@ -27,6 +31,8 @@
 #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
 {
@@ -49,6 +55,8 @@ const char * const HORIZONTAL_ALIGNMENT_PROPERTY( "horizontalAlignment" );
 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[] =
 {
@@ -98,14 +106,14 @@ const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
   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
@@ -131,19 +139,54 @@ const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
   }\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
 );
 
 /**
@@ -196,6 +239,14 @@ Dali::Property::Index StringKeyToIndexKey( const std::string& stringKey )
   {
     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;
 }
@@ -267,6 +318,12 @@ void TextVisual::DoCreatePropertyMap( Property::Map& map ) const
   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
@@ -282,7 +339,9 @@ 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 )
 {
 }
 
@@ -321,20 +380,29 @@ void TextVisual::DoSetOnStage( Actor& actor )
   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 )
@@ -356,7 +424,7 @@ void TextVisual::DoSetOffStage( Actor& actor )
 
 void TextVisual::OnSetTransform()
 {
-  UpdateRenderer( false );
+  UpdateRenderer();
 }
 
 void TextVisual::DoSetProperty( Dali::Property::Index index, const Dali::Property::Value& propertyValue )
@@ -431,10 +499,20 @@ void TextVisual::DoSetProperty( Dali::Property::Index index, const Dali::Propert
       }
       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 )
@@ -471,8 +549,11 @@ void TextVisual::UpdateRenderer( bool initializeRendererAndTexture )
 
   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();
 
@@ -485,39 +566,133 @@ void TextVisual::UpdateRenderer( bool initializeRendererAndTexture )
     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
@@ -545,6 +720,33 @@ void TextVisual::RemoveTextureSet()
   }
 }
 
+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
index 82222768e374c434359884f4515164f18a60fbe2..31353b92d444a2ba78a15a52fe18ee75cabf6539 100644 (file)
@@ -18,6 +18,9 @@
  *
  */
 
+// 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>
@@ -81,6 +84,35 @@ public:
    */
   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
 
   /**
@@ -151,19 +183,37 @@ private:
 
   /**
    * @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
index cc68e0ff152ec50cabd14068968597fb58dd6373..17d22467fc4c369d18b1bef55fe5764dfceaf8e8 100644 (file)
@@ -69,7 +69,8 @@ public:
     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
   };
index 567471697f34711c7076828ec38dc9c88e276aad..df0abce62d686e6f83f05654a9d2c927a2cd1069 100644 (file)
@@ -90,6 +90,7 @@ public:
     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