Reduce text visual memory consumption for text with no styles and emojis 89/148089/3
authorRichard Huang <r.huang@samsung.com>
Wed, 6 Sep 2017 16:37:37 +0000 (17:37 +0100)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Thu, 7 Sep 2017 15:18:57 +0000 (16:18 +0100)
Create single channel texture for text with no styles and emojis.

Change-Id: I14363763fe7c7422a2a555d8a9ba0b68e1ab575b

dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
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-model-interface.h
dali-toolkit/internal/text/text-model.cpp
dali-toolkit/internal/text/text-model.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

index d92b1cd..e871db3 100644 (file)
@@ -948,7 +948,7 @@ void TextLabel::SetUpAutoScrolling()
 
   // 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 ); // ignore the horizontal alignment
+  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(),
index 1821db4..66bb797 100644 (file)
@@ -59,11 +59,13 @@ struct GlyphData
  * @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( GlyphData& data,
                    const Vector2* const position,
                    const Vector4* const color,
-                   Typesetter::Style style)
+                   Typesetter::Style style,
+                   Pixel::Format pixelFormat )
 {
   if( ( 0u == data.glyphBitmap.width ) || ( 0u == data.glyphBitmap.height ) )
   {
@@ -74,81 +76,138 @@ void TypesetGlyph( 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;
-
-  // Pointer to the color glyph if there is one.
-  const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>( data.glyphBitmap.buffer ) : NULL;
+  if ( Pixel::RGBA8888 == pixelFormat )
+  {
+    // Whether the given glyph is a color one.
+    const bool isColorGlyph = Pixel::BGRA8888 == data.glyphBitmap.format;
 
-  // 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 );
+    // Pointer to the color glyph if there is one.
+    const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>( data.glyphBitmap.buffer ) : NULL;
 
-  // Initial vertical offset.
-  const int yOffset = data.verticalOffset + position->y;
+    // 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 );
 
-  // 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 ) )
-    {
-      // Do not write out of bounds.
-      break;
-    }
+    // 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;
       }
 
-      uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( data.bitmapBuffer.GetBuffer() );
-
-      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 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 )
+        const int xOffsetIndex = xOffset + index;
+        if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
         {
-          // 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.
+          // Don't write out of bounds.
+          break;
         }
 
-        // Update the alpha channel.
-        if( Typesetter::STYLE_MASK == style )
+        uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( data.bitmapBuffer.GetBuffer() );
+
+        if( isColorGlyph )
         {
-          // Create an alpha mask for color glyph.
-          *( packedColorGlyphBuffer + 3u ) = 0u;
+          // 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
         {
-          *( packedColorGlyphBuffer + 3u ) = static_cast<uint8_t>( color->a * static_cast<float>( *( packedColorGlyphBuffer + 3u ) ) );
+          // 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;
 
-        // Set the color into the final pixel buffer.
-        *( bitmapBuffer + verticalOffset + xOffsetIndex ) = packedColorGlyph;
+    // 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 ) )
+      {
+        // Do not write out of bounds.
+        break;
       }
-      else
+
+      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;
+        }
+
+        uint8_t* bitmapBuffer = reinterpret_cast< uint8_t* >( data.bitmapBuffer.GetBuffer() );
+
         // Update the alpha channel.
         const uint8_t alpha = *( data.glyphBitmap.buffer + glyphBufferOffset + index );
 
@@ -156,20 +215,15 @@ void TypesetGlyph( GlyphData& data,
         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& 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).
-          *( packedColorBuffer + 3u ) = std::max( currentAlpha, newAlpha );
-
-          // Set the color into the final pixel buffer.
-          currentColor = packedColor;
+          *( bitmapBuffer + verticalOffset + xOffsetIndex ) = std::max( currentAlpha, newAlpha );
         }
       }
     }
@@ -207,7 +261,7 @@ ViewModel* Typesetter::GetViewModel()
   return mModel;
 }
 
-PixelData Typesetter::Render( const Vector2& size, RenderBehaviour behaviour, bool ignoreHorizontalAlignment )
+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.
 
@@ -257,7 +311,7 @@ PixelData Typesetter::Render( const Vector2& size, RenderBehaviour behaviour, bo
   if( RENDER_MASK == behaviour )
   {
     // Generate the image buffer as an alpha mask for color glyphs.
-    imageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, penY, 0u, numberOfGlyphs - 1 );
+    imageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penY, 0u, numberOfGlyphs - 1 );
   }
   else if( RENDER_NO_TEXT == behaviour )
   {
@@ -268,7 +322,7 @@ PixelData Typesetter::Render( const Vector2& size, RenderBehaviour behaviour, bo
   else
   {
     // Generate the image buffer for the text with no style.
-    imageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, penY, 0u, numberOfGlyphs -1 );
+    imageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penY, 0u, numberOfGlyphs -1 );
   }
 
   if ( ( RENDER_NO_STYLES != behaviour ) && ( RENDER_MASK != behaviour ) )
@@ -280,7 +334,7 @@ PixelData Typesetter::Render( const Vector2& size, RenderBehaviour behaviour, bo
     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, penY, 0u, numberOfGlyphs - 1 );
+      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 );
@@ -291,7 +345,7 @@ PixelData Typesetter::Render( const Vector2& size, RenderBehaviour behaviour, bo
     if ( underlineEnabled )
     {
       // Create the image buffer for underline
-      Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, penY, 0u, numberOfGlyphs - 1 );
+      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 );
@@ -304,7 +358,7 @@ PixelData Typesetter::Render( const Vector2& size, RenderBehaviour behaviour, bo
   return pixelData;
 }
 
-Devel::PixelBuffer Typesetter::CreateImageBuffer( const unsigned int bufferWidth, const unsigned int bufferHeight, Typesetter::Style style, bool ignoreHorizontalAlignment, int verticalOffset, GlyphIndex fromGlyphIndex, GlyphIndex toGlyphIndex )
+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();
@@ -322,13 +376,20 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer( const unsigned int bufferWidth
   // Create and initialize the pixel buffer.
   GlyphData glyphData;
   glyphData.verticalOffset = verticalOffset;
-
   glyphData.width = bufferWidth;
   glyphData.height = bufferHeight;
-  const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
-  const unsigned int bufferSizeChar = 4u * bufferSizeInt;
-  glyphData.bitmapBuffer = Devel::PixelBuffer::New( bufferWidth, bufferHeight, Pixel::RGBA8888 );
-  memset( glyphData.bitmapBuffer.GetBuffer(), 0u, bufferSizeChar );
+  glyphData.bitmapBuffer = Devel::PixelBuffer::New( bufferWidth, bufferHeight, pixelFormat );
+
+  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();
@@ -490,7 +551,8 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer( const unsigned int bufferWidth
         TypesetGlyph( glyphData,
                       position,
                       color,
-                      style );
+                      style,
+                      pixelFormat);
         // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
         delete []glyphData.glyphBitmap.buffer;
         glyphData.glyphBitmap.buffer = NULL;
@@ -518,6 +580,7 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer( const unsigned int bufferWidth
             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 );
@@ -565,6 +628,7 @@ Devel::PixelBuffer Typesetter::CombineImageBuffer( Devel::PixelBuffer topPixelBu
     return topPixelBuffer;
   }
 
+  // Always combine two RGBA images
   const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
   const unsigned int bufferSizeChar = 4u * bufferSizeInt;
 
index c1408cc..285348e 100644 (file)
@@ -21,6 +21,7 @@
 // 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>
@@ -103,10 +104,11 @@ public:
    * @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, RenderBehaviour behaviour = RENDER_TEXT_AND_STYLES, bool ignoreHorizontalAlignment = false );
+  PixelData Render( const Vector2& size, RenderBehaviour behaviour = RENDER_TEXT_AND_STYLES, bool ignoreHorizontalAlignment = false, Pixel::Format pixelFormat = Pixel::RGBA8888 );
 
 private:
   /**
@@ -134,16 +136,17 @@ private:
    * @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, int verticalOffset, TextAbstraction::GlyphIndex fromGlyphIndex, TextAbstraction::GlyphIndex toGlyphIndex );
+  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 image buffers together.
+   * @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
@@ -159,7 +162,7 @@ private:
    * @return The combined image buffer with the text.
    *
    */
-  Devel::PixelBuffer CombineImageBuffer( Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight );
+  Devel::PixelBuffer CombineImageBuffer( Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeightbool );
 
 protected:
 
index 3a0d319..1dcda36 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() )
index df0e32c..e15fa9c 100644 (file)
@@ -97,6 +97,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()
    */
   virtual Length GetNumberOfGlyphs() const;
index d84b46a..38e1fa2 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
@@ -104,6 +105,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.
    *
    * @return The number of laid-out glyphs.
index 05cb405..2c00cf8 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();
index d8b08e9..a7b0bb8 100644 (file)
@@ -100,6 +100,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()
    */
   virtual Length GetNumberOfGlyphs() const;
index 46cb2a1..2968f07 100644 (file)
@@ -20,6 +20,7 @@
 
 // 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>
@@ -31,6 +32,7 @@
 #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
 {
@@ -129,7 +131,7 @@ const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
   }\n
 );
 
-const char* FRAGMENT_SHADER_ATLAS_CLAMP = DALI_COMPOSE_SHADER(
+const char* FRAGMENT_SHADER_ATLAS_CLAMP_RGBA = DALI_COMPOSE_SHADER(
   varying mediump vec2 vTexCoord;\n
   uniform sampler2D sTexture;\n
   uniform sampler2D sStyle;\n
@@ -160,6 +162,25 @@ const char* FRAGMENT_SHADER_ATLAS_CLAMP = DALI_COMPOSE_SHADER(
   }\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
+);
+
 /**
  * Return Property index for the given string key
  * param[in] stringKey the string index key
@@ -351,15 +372,7 @@ void TextVisual::DoSetOnStage( Actor& actor )
   mControl = actor;
 
   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
-
-  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 );
-
-    mFactoryCache.SaveShader( VisualFactoryCache::TEXT_SHADER, shader );
-  }
+  Shader shader = GetTextShader(mFactoryCache, true);
 
   mImpl->mRenderer = Renderer::New( geometry, shader );
   mImpl->mRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT );
@@ -545,66 +558,130 @@ void TextVisual::UpdateRenderer()
     if( ( relayoutSize.width > Math::MACHINE_EPSILON_1000 ) &&
         ( relayoutSize.height > Math::MACHINE_EPSILON_1000 ) )
     {
-      Vector4 atlasRect = FULL_TEXTURE_RECT;
+      // Check whether it is a markup text with multiple text colors
+      const Vector4* const colorsBuffer = mController->GetTextModel()->GetColors();
+      bool hasMultipleTextColors = ( NULL != colorsBuffer );
+
+      // Check whether the text contains any emoji
+      bool containsEmoji = false;
 
-      // Create a texture for the text without any styles
-      PixelData data = mTypesetter->Render( relayoutSize, Text::Typesetter::RENDER_NO_STYLES );
+      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
 
-      // 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 );
 
-      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 );
 
-      // Create a texture for all the text styles (without the text itself)
-      PixelData styleData = mTypesetter->Render( relayoutSize, Text::Typesetter::RENDER_NO_TEXT );
+        TextureSet textureSet = TextureSet::New();
+        textureSet.SetTexture( 0u, texture );
 
-      Texture styleTexture = Texture::New( Dali::TextureType::TEXTURE_2D,
-                                           styleData.GetPixelFormat(),
-                                           styleData.GetWidth(),
-                                           styleData.GetHeight() );
+        // Create a texture for all the text styles (without the text itself)
+        PixelData styleData = mTypesetter->Render( relayoutSize, Text::Typesetter::RENDER_NO_TEXT );
 
-      styleTexture.Upload( styleData );
+        Texture styleTexture = Texture::New( Dali::TextureType::TEXTURE_2D,
+                                             styleData.GetPixelFormat(),
+                                             styleData.GetWidth(),
+                                             styleData.GetHeight() );
 
-      textureSet.SetTexture( 1u, styleTexture );
+        styleTexture.Upload( styleData );
 
-      // 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 );
+        textureSet.SetTexture( 1u, styleTexture );
 
-      Texture maskTexture = Texture::New( Dali::TextureType::TEXTURE_2D,
-                                          styleData.GetPixelFormat(),
-                                          styleData.GetWidth(),
-                                          styleData.GetHeight() );
+        // 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 );
 
-      maskTexture.Upload( maskData );
+        Texture maskTexture = Texture::New( Dali::TextureType::TEXTURE_2D,
+                                            styleData.GetPixelFormat(),
+                                            styleData.GetWidth(),
+                                            styleData.GetHeight() );
 
-      textureSet.SetTexture( 2u, maskTexture );
+        maskTexture.Upload( maskData );
 
-      // 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 );
-      textureSet.SetSampler( 1u, sampler );
-      textureSet.SetSampler( 2u, sampler );
+        textureSet.SetTexture( 2u, maskTexture );
 
-      mImpl->mRenderer.SetTextures( textureSet );
+        // 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
+
+        // Create a texture for the text without any styles
+        PixelData data = mTypesetter->Render( relayoutSize, Text::Typesetter::RENDER_NO_STYLES, false, Pixel::L8 );
+
+        // 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 );
+
+        // 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 );
-
-      // Check whether it is a markup text with multiple text colors
-      const Vector4* const colorsBuffer = mController->GetTextModel()->GetColors();
-      bool hasMultipleTextColors = ( NULL != colorsBuffer );
       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 );
 
@@ -635,6 +712,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 2ed7bee..31353b9 100644 (file)
@@ -192,6 +192,13 @@ private:
   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
index cc68e0f..17d2246 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
   };