TextVisual Outline support 58/151358/3
authorAgnelo Vaz <agnelo.vaz@samsung.com>
Wed, 20 Sep 2017 13:21:55 +0000 (14:21 +0100)
committerAgnelo Vaz <agnelo.vaz@samsung.com>
Wed, 20 Sep 2017 16:30:50 +0000 (16:30 +0000)
Text outline support for TextVisual

Memory Consuption memps

Average over 10 runs.

200 TextLabels = 16112 bytes    80 bytes per TextLabel (pre this patch)
200 TextLabels = 16118 bytes    80 bytes per TextLabel (including this patch)
No significant change before and after this patch for Textlabel

200 TextLabels = 101931 bytes   509 bytes per TextLabel with outline
Styling increases memory usage as expected.

MemPS data

16112    80 bytes per TextLabel before patch
16116
16112
16112
16116
16112
16116
16108
16116
16112

161132/10 = 16113 = 80bytes

16120    80 bytes per TextLabel after patch
16116
16116
16120
16116
16116
16128
16116
16116
16120

161184/10 = 16118 = 80bytes

101832   509 bytes per TextLabel after patch with outline
101832
101860
101836
101828
101828
101828
101832
101832
102808

1019316/10 = 101931 = 509 bytes

Change-Id: I69aa79f6211a3fb076f557faf6f104b472fd0787

18 files changed:
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-text-abstraction.cpp [changed mode: 0644->0755]
automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextField.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp
dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp
dali-toolkit/internal/text/rendering/text-typesetter.cpp [changed mode: 0644->0755]
dali-toolkit/internal/text/rendering/view-model.cpp [changed mode: 0644->0755]
dali-toolkit/internal/text/rendering/view-model.h [changed mode: 0644->0755]
dali-toolkit/internal/text/text-controller.cpp [changed mode: 0644->0755]
dali-toolkit/internal/text/text-controller.h [changed mode: 0644->0755]
dali-toolkit/internal/text/text-effects-style.cpp [changed mode: 0644->0755]
dali-toolkit/internal/text/text-effects-style.h [changed mode: 0644->0755]
dali-toolkit/internal/text/text-model-interface.h [changed mode: 0644->0755]
dali-toolkit/internal/text/text-model.cpp [changed mode: 0644->0755]
dali-toolkit/internal/text/text-model.h [changed mode: 0644->0755]
dali-toolkit/internal/text/visual-model-impl.cpp [changed mode: 0644->0755]
dali-toolkit/internal/text/visual-model-impl.h [changed mode: 0644->0755]
dali-toolkit/internal/visuals/text/text-visual.cpp [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
index df25aaa..5cc9401
@@ -152,8 +152,8 @@ public:
   void GetFontMetrics( FontId fontId, FontMetrics& metrics ){}
   GlyphIndex GetGlyphIndex( FontId fontId, Character charcode ){return 0;}
   bool GetGlyphMetrics( GlyphInfo* array, uint32_t size, bool horizontal ){return true;}
   void GetFontMetrics( FontId fontId, FontMetrics& metrics ){}
   GlyphIndex GetGlyphIndex( FontId fontId, Character charcode ){return 0;}
   bool GetGlyphMetrics( GlyphInfo* array, uint32_t size, bool horizontal ){return true;}
-  void CreateBitmap( FontId fontId, GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data ){}
-  PixelData CreateBitmap( FontId fontId, GlyphIndex glyphIndex ){return PixelData();}
+  void CreateBitmap( FontId fontId, GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ){}
+  PixelData CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth ){return PixelData();}
   void CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob,
                          unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
   {
   void CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob,
                          unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
   {
@@ -472,14 +472,14 @@ bool FontClient::GetGlyphMetrics( GlyphInfo* array, uint32_t size, GlyphType typ
   return GetImplementation(*this).GetGlyphMetrics( array, size, horizontal );
 }
 
   return GetImplementation(*this).GetGlyphMetrics( array, size, horizontal );
 }
 
-void FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data )
+void FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth )
 {
 {
-  GetImplementation(*this).CreateBitmap( fontId, glyphIndex, data );
+  GetImplementation(*this).CreateBitmap( fontId, glyphIndex, data, outlineWidth );
 }
 
 }
 
-PixelData FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex )
+PixelData FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth )
 {
 {
-  return GetImplementation(*this).CreateBitmap( fontId, glyphIndex );
+  return GetImplementation(*this).CreateBitmap( fontId, glyphIndex, outlineWidth );
 }
 
 void FontClient::CreateVectorBlob( FontId fontId,
 }
 
 void FontClient::CreateVectorBlob( FontId fontId,
index 3fd4bd6..59925df 100644 (file)
@@ -732,8 +732,19 @@ int UtcDaliTextEditorSetPropertyP(void)
   DALI_TEST_EQUALS( editor.GetProperty<std::string>( TextEditor::Property::INPUT_EMBOSS ), std::string("Emboss input properties"), TEST_LOCATION );
 
   // Check the outline property
   DALI_TEST_EQUALS( editor.GetProperty<std::string>( TextEditor::Property::INPUT_EMBOSS ), std::string("Emboss input properties"), TEST_LOCATION );
 
   // Check the outline property
-  editor.SetProperty( TextEditor::Property::OUTLINE, "Outline properties" );
-  DALI_TEST_EQUALS( editor.GetProperty<std::string>( TextEditor::Property::OUTLINE ), std::string("Outline properties"), TEST_LOCATION );
+  Property::Map outlineMapSet;
+  Property::Map outlineMapGet;
+
+  outlineMapSet["color"] = Color::RED;
+  outlineMapSet["width"] = 2.0f;
+
+  editor.SetProperty( TextEditor::Property::OUTLINE, outlineMapSet );
+
+  outlineMapSet["color"] = "red";
+  outlineMapSet["width"] = "2";
+  outlineMapGet = editor.GetProperty<Property::Map>( TextEditor::Property::OUTLINE );
+  DALI_TEST_EQUALS( outlineMapGet.Count(), outlineMapSet.Count(), TEST_LOCATION );
+  DALI_TEST_EQUALS( DaliTestCheckMaps( outlineMapGet, outlineMapSet ), true, TEST_LOCATION );
 
   // Check the input outline property
   editor.SetProperty( TextEditor::Property::INPUT_OUTLINE, "Outline input properties" );
 
   // Check the input outline property
   editor.SetProperty( TextEditor::Property::INPUT_OUTLINE, "Outline input properties" );
index 08b194f..a7389b0 100644 (file)
@@ -827,8 +827,19 @@ int UtcDaliTextFieldSetPropertyP(void)
   DALI_TEST_EQUALS( field.GetProperty<std::string>( TextField::Property::INPUT_EMBOSS ), std::string("Emboss input properties"), TEST_LOCATION );
 
   // Check the outline property
   DALI_TEST_EQUALS( field.GetProperty<std::string>( TextField::Property::INPUT_EMBOSS ), std::string("Emboss input properties"), TEST_LOCATION );
 
   // Check the outline property
-  field.SetProperty( TextField::Property::OUTLINE, "Outline properties" );
-  DALI_TEST_EQUALS( field.GetProperty<std::string>( TextField::Property::OUTLINE ), std::string("Outline properties"), TEST_LOCATION );
+  Property::Map outlineMapSet;
+  Property::Map outlineMapGet;
+
+  outlineMapSet["color"] = Color::RED;
+  outlineMapSet["width"] = 2.0f;
+
+  field.SetProperty( TextField::Property::OUTLINE, outlineMapSet );
+
+  outlineMapSet["color"] = "red";
+  outlineMapSet["width"] = "2";
+  outlineMapGet = field.GetProperty<Property::Map>( TextField::Property::OUTLINE );
+  DALI_TEST_EQUALS( outlineMapGet.Count(), outlineMapSet.Count(), TEST_LOCATION );
+  DALI_TEST_EQUALS( DaliTestCheckMaps( outlineMapGet, outlineMapSet ), true, TEST_LOCATION );
 
   // Check the input outline property
   field.SetProperty( TextField::Property::INPUT_OUTLINE, "Outline input properties" );
 
   // Check the input outline property
   field.SetProperty( TextField::Property::INPUT_OUTLINE, "Outline input properties" );
index beba3a6..2f2e692 100644 (file)
@@ -434,8 +434,18 @@ int UtcDaliToolkitTextLabelSetPropertyP(void)
   DALI_TEST_EQUALS( label.GetProperty<std::string>( TextLabel::Property::EMBOSS ), std::string("Emboss properties"), TEST_LOCATION );
 
   // Check the outline property
   DALI_TEST_EQUALS( label.GetProperty<std::string>( TextLabel::Property::EMBOSS ), std::string("Emboss properties"), TEST_LOCATION );
 
   // Check the outline property
-  label.SetProperty( TextLabel::Property::OUTLINE, "Outline properties" );
-  DALI_TEST_EQUALS( label.GetProperty<std::string>( TextLabel::Property::OUTLINE ), std::string("Outline properties"), TEST_LOCATION );
+  Property::Map outlineMapSet;
+  Property::Map outlineMapGet;
+
+  outlineMapSet["color"] = Color::RED;
+  outlineMapSet["width"] = 2.0f;
+  label.SetProperty( TextLabel::Property::OUTLINE, outlineMapSet );
+
+  outlineMapSet["color"] = "red";
+  outlineMapSet["width"] = "2";
+  outlineMapGet = label.GetProperty<Property::Map>( TextLabel::Property::OUTLINE );
+  DALI_TEST_EQUALS( outlineMapGet.Count(), outlineMapSet.Count(), TEST_LOCATION );
+  DALI_TEST_EQUALS( DaliTestCheckMaps( outlineMapGet, outlineMapSet ), true, TEST_LOCATION );
 
   // Check the pixel size of font
   label.SetProperty( DevelTextLabel::Property::PIXEL_SIZE, 20.f );
 
   // Check the pixel size of font
   label.SetProperty( DevelTextLabel::Property::PIXEL_SIZE, 20.f );
@@ -990,3 +1000,71 @@ int UtcDaliToolkitTextLabelColorComponents(void)
 
   END_TEST;
 }
 
   END_TEST;
 }
+
+int UtcDaliToolkitTextlabelTextStyle01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextlabelTextStyle Setting Outline after Shadow");
+
+  TextLabel label = TextLabel::New();
+  label.SetSize( 300.0f, 300.f );
+  label.SetProperty( TextLabel::Property::TEXT, "Hello world Hello world" );
+  label.SetProperty( TextLabel::Property::POINT_SIZE, 12 );
+  Stage::GetCurrent().Add( label );
+
+  Property::Map outlineMapSet;
+  Property::Map outlineMapGet;
+
+  outlineMapSet["color"] = Color::BLUE;
+  outlineMapSet["width"] = 2.0f;
+  label.SetProperty( TextLabel::Property::OUTLINE, outlineMapSet );
+
+  application.SendNotification();
+  application.Render();
+
+  Property::Map shadowMapSet;
+  shadowMapSet.Insert( "color", "green" );
+  shadowMapSet.Insert( "offset", "2 2" );
+  label.SetProperty( TextLabel::Property::SHADOW, shadowMapSet );
+
+  outlineMapSet["color"] = Color::RED;
+  outlineMapSet["width"] = 0.0f;
+  label.SetProperty( TextLabel::Property::OUTLINE, outlineMapSet );
+
+  application.SendNotification();
+  application.Render();
+
+  outlineMapGet = label.GetProperty<Property::Map>( TextLabel::Property::OUTLINE );
+
+  Property::Value* colorValue = outlineMapGet.Find("color");
+
+  bool colorMatched( false );
+
+  if ( colorValue )
+  {
+     Property::Type valueType = colorValue->GetType();
+
+     if ( Property::STRING == valueType )
+     {
+       std::string stringValue;
+       colorValue->Get( stringValue );
+       if (  stringValue == "red" )
+       {
+         colorMatched = true;
+       }
+     }
+     else if ( Property::VECTOR4 == valueType )
+     {
+       Vector4 colorVector4;
+       colorValue->Get( colorVector4 );
+       if (  colorVector4 == Color::RED )
+       {
+         colorMatched = true;
+       }
+     }
+  }
+
+  DALI_TEST_EQUALS( colorMatched, true, TEST_LOCATION );
+
+  END_TEST;
+}
index 13eeae6..62865d6 100644 (file)
@@ -94,6 +94,7 @@ const float HALF( 0.5f );
 const float ONE( 1.0f );
 const uint32_t DEFAULT_ATLAS_WIDTH = 512u;
 const uint32_t DEFAULT_ATLAS_HEIGHT = 512u;
 const float ONE( 1.0f );
 const uint32_t DEFAULT_ATLAS_WIDTH = 512u;
 const uint32_t DEFAULT_ATLAS_HEIGHT = 512u;
+const int NO_OUTLINE( 0 );
 }
 
 struct AtlasRenderer::Impl
 }
 
 struct AtlasRenderer::Impl
@@ -354,7 +355,8 @@ struct AtlasRenderer::Impl
 
           mFontClient.CreateBitmap( glyph.fontId,
                                     glyph.index,
 
           mFontClient.CreateBitmap( glyph.fontId,
                                     glyph.index,
-                                    glyphBufferData );
+                                    glyphBufferData,
+                                    NO_OUTLINE );
 
           // Create the pixel data.
           bitmap = PixelData::New( glyphBufferData.buffer,
 
           // Create the pixel data.
           bitmap = PixelData::New( glyphBufferData.buffer,
old mode 100644 (file)
new mode 100755 (executable)
index 66bb797..b524710
@@ -102,7 +102,7 @@ void TypesetGlyph( GlyphData& data,
       if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
       {
         // Do not write out of bounds.
       if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
       {
         // Do not write out of bounds.
-        break;
+        continue;
       }
 
       const int verticalOffset = yOffsetIndex * data.width;
       }
 
       const int verticalOffset = yOffsetIndex * data.width;
@@ -114,7 +114,7 @@ void TypesetGlyph( GlyphData& data,
         if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
         {
           // Don't write out of bounds.
         if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
         {
           // Don't write out of bounds.
-          break;
+          continue;
         }
 
         uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( data.bitmapBuffer.GetBuffer() );
         }
 
         uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( data.bitmapBuffer.GetBuffer() );
@@ -138,7 +138,7 @@ void TypesetGlyph( GlyphData& data,
           }
 
           // Update the alpha channel.
           }
 
           // Update the alpha channel.
-          if( Typesetter::STYLE_MASK == style )
+          if( Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style ) // Outline not shown for color glyph
           {
             // Create an alpha mask for color glyph.
             *( packedColorGlyphBuffer + 3u ) = 0u;
           {
             // Create an alpha mask for color glyph.
             *( packedColorGlyphBuffer + 3u ) = 0u;
@@ -191,7 +191,7 @@ void TypesetGlyph( GlyphData& data,
       if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
       {
         // Do not write out of bounds.
       if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
       {
         // Do not write out of bounds.
-        break;
+        continue;
       }
 
       const int verticalOffset = yOffsetIndex * data.width;
       }
 
       const int verticalOffset = yOffsetIndex * data.width;
@@ -203,7 +203,7 @@ void TypesetGlyph( GlyphData& data,
         if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
         {
           // Don't write out of bounds.
         if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
         {
           // Don't write out of bounds.
-          break;
+          continue;
         }
 
         uint8_t* bitmapBuffer = reinterpret_cast< uint8_t* >( data.bitmapBuffer.GetBuffer() );
         }
 
         uint8_t* bitmapBuffer = reinterpret_cast< uint8_t* >( data.bitmapBuffer.GetBuffer() );
@@ -217,7 +217,6 @@ void TypesetGlyph( GlyphData& data,
           // Check alpha of overlapped pixels
           uint8_t& currentAlpha = *( bitmapBuffer + verticalOffset + xOffsetIndex );
           uint8_t newAlpha = static_cast<uint8_t>( color->a * static_cast<float>( alpha ) );
           // 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
 
           // 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
@@ -327,6 +326,18 @@ PixelData Typesetter::Render( const Vector2& size, RenderBehaviour behaviour, bo
 
   if ( ( RENDER_NO_STYLES != behaviour ) && ( RENDER_MASK != behaviour ) )
   {
 
   if ( ( RENDER_NO_STYLES != behaviour ) && ( RENDER_MASK != behaviour ) )
   {
+
+    // Generate the outline if enabled
+    const float outlineWidth = mModel->GetOutlineWidth();
+    if ( outlineWidth > Math::MACHINE_EPSILON_1 )
+    {
+      // Create the image buffer for outline
+      Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penY, 0u, numberOfGlyphs -1 );
+
+      // Combine the two buffers
+      imageBuffer = CombineImageBuffer( imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight );
+    }
+
     // @todo. Support shadow and underline for partial text later on.
 
     // Generate the shadow if enabled
     // @todo. Support shadow and underline for partial text later on.
 
     // Generate the shadow if enabled
@@ -379,6 +390,7 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer( const unsigned int bufferWidth
   glyphData.width = bufferWidth;
   glyphData.height = bufferHeight;
   glyphData.bitmapBuffer = Devel::PixelBuffer::New( bufferWidth, bufferHeight, pixelFormat );
   glyphData.width = bufferWidth;
   glyphData.height = bufferHeight;
   glyphData.bitmapBuffer = Devel::PixelBuffer::New( bufferWidth, bufferHeight, pixelFormat );
+  glyphData.horizontalOffset = 0;
 
   if ( Pixel::RGBA8888 == pixelFormat )
   {
 
   if ( Pixel::RGBA8888 == pixelFormat )
   {
@@ -405,14 +417,27 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer( const unsigned int bufferWidth
     // Increases the vertical offset with the line's ascender.
     glyphData.verticalOffset += static_cast<int>( line.ascender );
 
     // Increases the vertical offset with the line's ascender.
     glyphData.verticalOffset += static_cast<int>( line.ascender );
 
-    if ( style == Typesetter::STYLE_SHADOW )
+    // Retrieves the glyph's outline width
+    float outlineWidth = mModel->GetOutlineWidth();
+
+    if( style == Typesetter::STYLE_OUTLINE )
+    {
+      glyphData.horizontalOffset -= outlineWidth;
+      if( lineIndex == 0u )
+      {
+        // Only need to add the vertical outline offset for the first line
+        glyphData.verticalOffset -= outlineWidth;
+      }
+    }
+    else if ( style == Typesetter::STYLE_SHADOW )
     {
       const Vector2& shadowOffset = mModel->GetShadowOffset();
     {
       const Vector2& shadowOffset = mModel->GetShadowOffset();
-      glyphData.horizontalOffset += shadowOffset.x;
+      glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
+
       if ( lineIndex == 0u )
       {
       if ( lineIndex == 0u )
       {
-        // Only need to add the vertical shadow offset for once
-        glyphData.verticalOffset += shadowOffset.y;
+        // Only need to add the vertical shadow offset for first line
+        glyphData.verticalOffset += shadowOffset.y - outlineWidth;
       }
     }
 
       }
     }
 
@@ -532,6 +557,10 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer( const unsigned int bufferWidth
       {
         color = &( mModel->GetShadowColor() );
       }
       {
         color = &( mModel->GetShadowColor() );
       }
+      else if ( style == Typesetter::STYLE_OUTLINE )
+      {
+        color = &( mModel->GetOutlineColor() );
+      }
       else
       {
         color = ( useDefaultColor || ( 0u == colorIndex ) ) ? &defaultColor : colorsBuffer + ( colorIndex - 1u );
       else
       {
         color = ( useDefaultColor || ( 0u == colorIndex ) ) ? &defaultColor : colorsBuffer + ( colorIndex - 1u );
@@ -541,9 +570,17 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer( const unsigned int bufferWidth
       glyphData.glyphBitmap.buffer = NULL;
       glyphData.glyphBitmap.width = glyphInfo->width;   // Desired width and height.
       glyphData.glyphBitmap.height = glyphInfo->height;
       glyphData.glyphBitmap.buffer = NULL;
       glyphData.glyphBitmap.width = glyphInfo->width;   // Desired width and height.
       glyphData.glyphBitmap.height = glyphInfo->height;
+
+      if( style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW )
+      {
+        // Don't render outline for other styles
+        outlineWidth = 0.0f;
+      }
+
       fontClient.CreateBitmap( glyphInfo->fontId,
                                glyphInfo->index,
       fontClient.CreateBitmap( glyphInfo->fontId,
                                glyphInfo->index,
-                               glyphData.glyphBitmap );
+                               glyphData.glyphBitmap,
+                               outlineWidth );
 
       // Sets the glyph's bitmap into the bitmap of the whole text.
       if( NULL != glyphData.glyphBitmap.buffer )
 
       // Sets the glyph's bitmap into the bitmap of the whole text.
       if( NULL != glyphData.glyphBitmap.buffer )
old mode 100644 (file)
new mode 100755 (executable)
index 1dcda36..fe3c451
@@ -188,6 +188,16 @@ void ViewModel::GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex ind
   mModel->GetUnderlineRuns( underlineRuns, index, numberOfRuns );
 }
 
   mModel->GetUnderlineRuns( underlineRuns, index, numberOfRuns );
 }
 
+const Vector4& ViewModel::GetOutlineColor() const
+{
+  return mModel->GetOutlineColor();
+}
+
+float ViewModel::GetOutlineWidth() const
+{
+  return mModel->GetOutlineWidth();
+}
+
 void ViewModel::ElideGlyphs()
 {
   mIsTextElided = false;
 void ViewModel::ElideGlyphs()
 {
   mIsTextElided = false;
old mode 100644 (file)
new mode 100755 (executable)
index e15fa9c..827ecf0
@@ -172,6 +172,16 @@ public:
   virtual void GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const;
 
   /**
   virtual void GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const;
 
   /**
+   * @copydoc ModelInterface::GetOutlineColor()
+   */
+  virtual const Vector4& GetOutlineColor() const;
+
+  /**
+   * @copydoc ModelInterface::GetOutlineWidth()
+   */
+  virtual float GetOutlineWidth() const;
+
+ /**
    * @brief Does the text elide.
    *
    * It stores a copy of the visible glyphs and removes as many glyphs as needed
    * @brief Does the text elide.
    *
    * It stores a copy of the visible glyphs and removes as many glyphs as needed
old mode 100644 (file)
new mode 100755 (executable)
index 258fea7..482cfc3
@@ -1129,6 +1129,30 @@ float Controller::GetUnderlineHeight() const
   return mImpl->mModel->mVisualModel->GetUnderlineHeight();
 }
 
   return mImpl->mModel->mVisualModel->GetUnderlineHeight();
 }
 
+void Controller::SetOutlineColor( const Vector4& color )
+{
+  mImpl->mModel->mVisualModel->SetOutlineColor( color );
+
+  mImpl->RequestRelayout();
+}
+
+const Vector4& Controller::GetOutlineColor() const
+{
+  return mImpl->mModel->mVisualModel->GetOutlineColor();
+}
+
+void Controller::SetOutlineWidth( float width )
+{
+  mImpl->mModel->mVisualModel->SetOutlineWidth( width );
+
+  mImpl->RequestRelayout();
+}
+
+float Controller::GetOutlineWidth() const
+{
+  return mImpl->mModel->mVisualModel->GetOutlineWidth();
+}
+
 void Controller::SetDefaultEmbossProperties( const std::string& embossProperties )
 {
   if( NULL == mImpl->mEmbossDefaults )
 void Controller::SetDefaultEmbossProperties( const std::string& embossProperties )
 {
   if( NULL == mImpl->mEmbossDefaults )
old mode 100644 (file)
new mode 100755 (executable)
index 9e193c7..3d0eb6c
@@ -777,6 +777,34 @@ public: // Default style & Input style
   float GetUnderlineHeight() const;
 
   /**
   float GetUnderlineHeight() const;
 
   /**
+   * @brief Set the outline color.
+   *
+   * @param[in] color color of outline.
+   */
+  void SetOutlineColor( const Vector4& color );
+
+  /**
+   * @brief Retrieve the outline color.
+   *
+   * @return The outline color.
+   */
+  const Vector4& GetOutlineColor() const;
+
+  /**
+   * @brief Set the outline width
+   *
+   * @param[in] width The width in pixels of the outline, 0 indicates no outline
+   */
+  void SetOutlineWidth( float width );
+
+  /**
+   * @brief Retrieves the width of an outline
+   *
+   * @return The width of the outline.
+   */
+  float GetOutlineWidth() const;
+
+  /**
    * @brief Sets the emboss's properties string.
    *
    * @note The string is stored to be recovered.
    * @brief Sets the emboss's properties string.
    *
    * @note The string is stored to be recovered.
old mode 100644 (file)
new mode 100755 (executable)
index 75fabd7..8b71d30
@@ -35,6 +35,7 @@ namespace
 {
 const std::string COLOR_KEY( "color" );
 const std::string OFFSET_KEY( "offset" );
 {
 const std::string COLOR_KEY( "color" );
 const std::string OFFSET_KEY( "offset" );
+const std::string WIDTH_KEY( "width" );
 const std::string HEIGHT_KEY( "height" );
 const std::string ENABLE_KEY( "enable" );
 const std::string TRUE_TOKEN( "true" );
 const std::string HEIGHT_KEY( "height" );
 const std::string ENABLE_KEY( "enable" );
 const std::string TRUE_TOKEN( "true" );
@@ -120,6 +121,36 @@ bool ParseUnderlineProperties( const Property::Map& underlinePropertiesMap,
   return 0u == numberOfItems;
 }
 
   return 0u == numberOfItems;
 }
 
+bool ParseOutlineProperties( const Property::Map& underlinePropertiesMap,
+                               bool& colorDefined,
+                               Vector4& color,
+                               bool& widthDefined,
+                               float& width )
+{
+  const unsigned int numberOfItems = underlinePropertiesMap.Count();
+
+  // Parses and applies the style.
+  for( unsigned int index = 0u; index < numberOfItems; ++index )
+  {
+    const KeyValuePair& valueGet = underlinePropertiesMap.GetKeyValue( index );
+
+    if( COLOR_KEY == valueGet.first.stringKey )
+    {
+      /// Color key.
+      colorDefined = true;
+      color = valueGet.second.Get<Vector4>();
+    }
+    else if( WIDTH_KEY == valueGet.first.stringKey )
+    {
+      /// Width key.
+      widthDefined = true;
+      width = valueGet.second.Get<float>();
+    }
+  }
+
+  return 0u == numberOfItems;
+}
+
 bool SetUnderlineProperties( ControllerPtr controller, const Property::Value& value, EffectStyle::Type type )
 {
   bool update = false;
 bool SetUnderlineProperties( ControllerPtr controller, const Property::Value& value, EffectStyle::Type type )
 {
   bool update = false;
@@ -463,24 +494,63 @@ bool SetOutlineProperties( ControllerPtr controller, const Property::Value& valu
 
   if( controller )
   {
 
   if( controller )
   {
-    const std::string properties = value.Get< std::string >();
-
     switch( type )
     {
       case EffectStyle::DEFAULT:
       {
     switch( type )
     {
       case EffectStyle::DEFAULT:
       {
-        // Stores the default outline's properties string to be recovered by the GetOutlineProperties() function.
-        controller->SetDefaultOutlineProperties( properties );
+        const Property::Map& propertiesMap = value.Get<Property::Map>();
+
+        bool colorDefined = false;
+        Vector4 color;
+        bool widthDefined = false;
+        float width = 0.f;
+
+        bool empty = true;
+
+        if ( !propertiesMap.Empty() )
+        {
+           empty = ParseOutlineProperties( propertiesMap,
+                                           colorDefined,
+                                           color,
+                                           widthDefined,
+                                           width );
+        }
+
+        if( !empty )
+        {
+          // Sets the default outline values.
+          if( colorDefined && ( controller->GetOutlineColor() != color ) )
+          {
+            controller->SetOutlineColor( color );
+            update = true;
+          }
+
+          if( widthDefined && ( fabsf( controller->GetOutlineWidth() - width ) > Math::MACHINE_EPSILON_1000 ) )
+          {
+            controller->SetOutlineWidth( width );
+            update = true;
+          }
+        }
+        else
+        {
+          // Disable outline
+          if( fabsf( controller->GetOutlineWidth() ) > Math::MACHINE_EPSILON_1000 )
+          {
+            controller->SetOutlineWidth( 0.0f );
+            update = true;
+          }
+        }
         break;
       }
       case EffectStyle::INPUT:
       {
         break;
       }
       case EffectStyle::INPUT:
       {
-        // Stores the input outline's properties string to be recovered by the GetOutlineProperties() function.
-        controller->SetInputOutlineProperties( properties );
+        const std::string& outlineProperties = value.Get<std::string>();
+
+        controller->SetInputOutlineProperties( outlineProperties );
         break;
       }
         break;
       }
-    }
-  }
+    } // switch
+  } // if( controller )
 
   return update;
 }
 
   return update;
 }
@@ -493,7 +563,21 @@ void GetOutlineProperties( ControllerPtr controller, Property::Value& value, Eff
     {
       case EffectStyle::DEFAULT:
       {
     {
       case EffectStyle::DEFAULT:
       {
-        value = controller->GetDefaultOutlineProperties();
+        const Vector4& color = controller->GetOutlineColor();
+        const float width = controller->GetOutlineWidth();
+
+        Property::Map map;
+
+        std::string colorStr;
+        Vector4ToColorString( color, colorStr );
+        map.Insert( COLOR_KEY, colorStr );
+
+        std::string widthStr;
+        FloatToString( width, widthStr );
+        map.Insert( WIDTH_KEY, widthStr );
+
+        value = map;
+
         break;
       }
       case EffectStyle::INPUT:
         break;
       }
       case EffectStyle::INPUT:
old mode 100644 (file)
new mode 100755 (executable)
index 4ab61a3..521f52c
@@ -72,6 +72,21 @@ bool ParseUnderlineProperties( const Property::Map& underlineProperties,
                                float& height );
 
 /**
                                float& height );
 
 /**
+ * @brief Parses the outline properties.
+ *
+ * @param[in] outlineProperties The map with the outline properties.
+ * @param[out] colorDefined Whether the outline's color is defined.
+ * @param[out] color The outline's color.
+ * @param[out] widthDefined Whether the outline's width is defined.
+ * @param[out] width The outline's width.
+ */
+bool ParseOutlineProperties( const Property::Map& outlineProperties,
+                               bool& colorDefined,
+                               Vector4& color,
+                               bool& widthDefined,
+                               float& width );
+
+/**
  * @brief Sets the underline properties.
  *
  * @param[in] controller The text's controller.
  * @brief Sets the underline properties.
  *
  * @param[in] controller The text's controller.
old mode 100644 (file)
new mode 100755 (executable)
index 38e1fa2..18cb7cc
@@ -210,6 +210,21 @@ public:
    * @param[in] numberOfRuns Number of underline runs to be copied.
    */
   virtual void GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const = 0;
    * @param[in] numberOfRuns Number of underline runs to be copied.
    */
   virtual void GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const = 0;
+
+  /**
+   * @brief Retrieve the outline color.
+   *
+   * @return The outline color.
+   */
+  virtual const Vector4& GetOutlineColor() const = 0;
+
+  /**
+   * @brief Retrieves the width of an outline
+   *
+   * @return The width of the outline.
+   */
+  virtual float GetOutlineWidth() const = 0;
+
 };
 
 } // namespace Text
 };
 
 } // namespace Text
old mode 100644 (file)
new mode 100755 (executable)
index 2c00cf8..cf6f35c
@@ -147,6 +147,16 @@ void Model::GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index,
   mVisualModel->GetUnderlineRuns( underlineRuns, index, numberOfRuns );
 }
 
   mVisualModel->GetUnderlineRuns( underlineRuns, index, numberOfRuns );
 }
 
+const Vector4& Model::GetOutlineColor() const
+{
+  return mVisualModel->GetOutlineColor();
+}
+
+float Model::GetOutlineWidth() const
+{
+  return mVisualModel->GetOutlineWidth();
+}
+
 Model::Model()
 : mLogicalModel(),
   mVisualModel(),
 Model::Model()
 : mLogicalModel(),
   mVisualModel(),
old mode 100644 (file)
new mode 100755 (executable)
index a7b0bb8..cd49581
@@ -174,6 +174,16 @@ public:
    */
   virtual void GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const;
 
    */
   virtual void GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const;
 
+  /**
+   * @copydoc ModelInterface::GetOutlineColor()
+   */
+  virtual const Vector4& GetOutlineColor() const;
+
+  /**
+   * @copydoc ModelInterface::GetOutlineWidth()
+   */
+  virtual float GetOutlineWidth() const;
+
 private: // Private contructors & copy operator.
 
   /**
 private: // Private contructors & copy operator.
 
   /**
old mode 100644 (file)
new mode 100755 (executable)
index 1b114c4..3e3f2d6
@@ -350,6 +350,11 @@ void VisualModel::SetUnderlineColor( const Vector4& color )
   mUnderlineColorSet = true;
 }
 
   mUnderlineColorSet = true;
 }
 
+void VisualModel::SetOutlineColor( const Vector4& color )
+{
+  mOutlineColor = color;
+}
+
 void VisualModel::SetUnderlineEnabled( bool enabled )
 {
   mUnderlineEnabled = enabled;
 void VisualModel::SetUnderlineEnabled( bool enabled )
 {
   mUnderlineEnabled = enabled;
@@ -360,6 +365,11 @@ void VisualModel::SetUnderlineHeight( float height )
   mUnderlineHeight = height;
 }
 
   mUnderlineHeight = height;
 }
 
+void VisualModel::SetOutlineWidth( float width )
+{
+  mOutlineWidth = width;
+}
+
 const Vector4& VisualModel::GetTextColor() const
 {
   return mTextColor;
 const Vector4& VisualModel::GetTextColor() const
 {
   return mTextColor;
@@ -380,6 +390,11 @@ const Vector4& VisualModel::GetUnderlineColor() const
   return mUnderlineColor;
 }
 
   return mUnderlineColor;
 }
 
+const Vector4& VisualModel::GetOutlineColor() const
+{
+  return mOutlineColor;
+}
+
 bool VisualModel::IsUnderlineEnabled() const
 {
   return mUnderlineEnabled;
 bool VisualModel::IsUnderlineEnabled() const
 {
   return mUnderlineEnabled;
@@ -390,6 +405,11 @@ float VisualModel::GetUnderlineHeight() const
   return mUnderlineHeight;
 }
 
   return mUnderlineHeight;
 }
 
+float VisualModel::GetOutlineWidth() const
+{
+  return mOutlineWidth;
+}
+
 Length VisualModel::GetNumberOfUnderlineRuns() const
 {
   return mUnderlineRuns.Count();
 Length VisualModel::GetNumberOfUnderlineRuns() const
 {
   return mUnderlineRuns.Count();
@@ -415,9 +435,11 @@ VisualModel::VisualModel()
   mTextColor( Color::BLACK ),
   mShadowColor( Color::BLACK ),
   mUnderlineColor( Color::BLACK ),
   mTextColor( Color::BLACK ),
   mShadowColor( Color::BLACK ),
   mUnderlineColor( Color::BLACK ),
+  mOutlineColor( Color::WHITE ),
   mControlSize(),
   mShadowOffset(),
   mUnderlineHeight( 0.0f ),
   mControlSize(),
   mShadowOffset(),
   mUnderlineHeight( 0.0f ),
+  mOutlineWidth( 0.0f ),
   mNaturalSize(),
   mLayoutSize(),
   mCachedLineIndex( 0u ),
   mNaturalSize(),
   mLayoutSize(),
   mCachedLineIndex( 0u ),
old mode 100644 (file)
new mode 100755 (executable)
index 6356f32..ea888cd
@@ -287,6 +287,34 @@ public:
    */
   Length GetNumberOfUnderlineRuns() const;
 
    */
   Length GetNumberOfUnderlineRuns() const;
 
+  /**
+   * @brief Set the outline color.
+   *
+   * @param[in] color color of outline.
+   */
+  void SetOutlineColor( const Vector4& color );
+
+  /**
+   * @brief Retrieve the outline color.
+   *
+   * @return The outline color.
+   */
+  const Vector4& GetOutlineColor() const;
+
+  /**
+   * @brief Set the outline width
+   *
+   * @param[in] width The width in pixels of the outline, 0 indicates no outline
+   */
+  void SetOutlineWidth( float width );
+
+  /**
+   * @brief Retrieves the width of an outline
+   *
+   * @return The width of the outline.
+   */
+  float GetOutlineWidth() const;
+
 protected:
 
   /**
 protected:
 
   /**
@@ -323,9 +351,11 @@ public:
   Vector4                mTextColor;            ///< The text color
   Vector4                mShadowColor;          ///< Color of drop shadow
   Vector4                mUnderlineColor;       ///< Color of underline
   Vector4                mTextColor;            ///< The text color
   Vector4                mShadowColor;          ///< Color of drop shadow
   Vector4                mUnderlineColor;       ///< Color of underline
+  Vector4                mOutlineColor;         ///< Color of outline
   Size                   mControlSize;          ///< The size of the UI control.
   Vector2                mShadowOffset;         ///< Offset for drop shadow, 0 indicates no shadow
   float                  mUnderlineHeight;      ///< Fixed height for underline to override font metrics.
   Size                   mControlSize;          ///< The size of the UI control.
   Vector2                mShadowOffset;         ///< Offset for drop shadow, 0 indicates no shadow
   float                  mUnderlineHeight;      ///< Fixed height for underline to override font metrics.
+  float                  mOutlineWidth;         ///< Width of outline.
 
 private:
 
 
 private:
 
old mode 100644 (file)
new mode 100755 (executable)
index c90c727..4031f94
@@ -586,6 +586,7 @@ void TextVisual::UpdateRenderer()
       }
 
       // Check whether the text contains any style colors (e.g. underline color, shadow color, etc.)
       }
 
       // 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 )
       bool shadowEnabled = false;
       const Vector2& shadowOffset = mController->GetTextModel()->GetShadowOffset();
       if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 )
@@ -593,9 +594,16 @@ void TextVisual::UpdateRenderer()
         shadowEnabled = true;
       }
 
         shadowEnabled = true;
       }
 
+      bool outlineWidthEnabled = false;
+      float outlineWidth = mController->GetTextModel()->GetOutlineWidth();
+      if ( outlineWidth > Math::MACHINE_EPSILON_1 )
+      {
+        outlineWidthEnabled = true;
+      }
+
       const bool underlineEnabled = mController->GetTextModel()->IsUnderlineEnabled();
 
       const bool underlineEnabled = mController->GetTextModel()->IsUnderlineEnabled();
 
-      if ( hasMultipleTextColors || containsEmoji || shadowEnabled || underlineEnabled )
+      if ( hasMultipleTextColors || containsEmoji || shadowEnabled || underlineEnabled || outlineWidthEnabled )
       {
         // Create RGBA textures if the text contains emojis or styles or multiple text colors
 
       {
         // Create RGBA textures if the text contains emojis or styles or multiple text colors