From 5d9d55db30babd7cb6662b1cab287c2ff74430d9 Mon Sep 17 00:00:00 2001 From: Richard Huang Date: Wed, 6 Sep 2017 17:37:37 +0100 Subject: [PATCH] Reduce text visual memory consumption for text with no styles and emojis Create single channel texture for text with no styles and emojis. Change-Id: I14363763fe7c7422a2a555d8a9ba0b68e1ab575b --- .../controls/text-controls/text-label-impl.cpp | 2 +- .../internal/text/rendering/text-typesetter.cpp | 214 +++++++++++++-------- .../internal/text/rendering/text-typesetter.h | 11 +- .../internal/text/rendering/view-model.cpp | 10 + dali-toolkit/internal/text/rendering/view-model.h | 10 + dali-toolkit/internal/text/text-model-interface.h | 15 ++ dali-toolkit/internal/text/text-model.cpp | 10 + dali-toolkit/internal/text/text-model.h | 10 + dali-toolkit/internal/visuals/text/text-visual.cpp | 202 ++++++++++++++----- dali-toolkit/internal/visuals/text/text-visual.h | 7 + .../internal/visuals/visual-factory-cache.h | 3 +- 11 files changed, 364 insertions(+), 130 deletions(-) diff --git a/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp b/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp index d92b1cd..e871db3 100644 --- a/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp +++ b/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp @@ -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(), diff --git a/dali-toolkit/internal/text/rendering/text-typesetter.cpp b/dali-toolkit/internal/text/rendering/text-typesetter.cpp index 1821db4..66bb797 100644 --- a/dali-toolkit/internal/text/rendering/text-typesetter.cpp +++ b/dali-toolkit/internal/text/rendering/text-typesetter.cpp @@ -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( data.width - 1u ); const int heightMinusOne = static_cast( 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( 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( &packedColor ); - *( packedColorBuffer + 2 ) = static_cast( color->b * 255.f ); - *( packedColorBuffer + 1 ) = static_cast( color->g * 255.f ); - *packedColorBuffer = static_cast( color->r * 255.f ); + // Pointer to the color glyph if there is one. + const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast( 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( &packedColor ); + *( packedColorBuffer + 2 ) = static_cast( color->b * 255.f ); + *( packedColorBuffer + 1 ) = static_cast( color->g * 255.f ); + *packedColorBuffer = static_cast( color->r * 255.f ); - // Traverse the pixels of the glyph line per line. - for( int lineIndex = 0, glyphHeight = static_cast( 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( data.glyphBitmap.width ); - for( int index = 0, glyphWidth = static_cast( data.glyphBitmap.width ); index < glyphWidth; ++index ) + // Traverse the pixels of the glyph line per line. + for( int lineIndex = 0, glyphHeight = static_cast( 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( data.glyphBitmap.width ); + for( int index = 0, glyphWidth = static_cast( 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( &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( color->b * 255.f ); - *( packedColorGlyphBuffer + 1 ) = static_cast( color->g * 255.f ); - *packedColorGlyphBuffer = static_cast( 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( &packedColorGlyph ); + + if( Typesetter::STYLE_SHADOW == style ) + { + // The shadow of color glyph needs to have the shadow color. + *( packedColorGlyphBuffer + 2 ) = static_cast( color->b * 255.f ); + *( packedColorGlyphBuffer + 1 ) = static_cast( color->g * 255.f ); + *packedColorGlyphBuffer = static_cast( 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( color->a * static_cast( *( packedColorGlyphBuffer + 3u ) ) ); + } + + // Set the color into the final pixel buffer. + *( bitmapBuffer + verticalOffset + xOffsetIndex ) = packedColorGlyph; } else { - *( packedColorGlyphBuffer + 3u ) = static_cast( color->a * static_cast( *( 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( ¤tColor ); + + uint8_t currentAlpha = *( packedCurrentColorBuffer + 3u ); + uint8_t newAlpha = static_cast( color->a * static_cast( 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( 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( data.glyphBitmap.width ); + for( int index = 0, glyphWidth = static_cast( 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( ¤tColor ); - - uint8_t currentAlpha = *( packedCurrentColorBuffer + 3u ); + uint8_t& currentAlpha = *( bitmapBuffer + verticalOffset + xOffsetIndex ); uint8_t newAlpha = static_cast( color->a * static_cast( 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( &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; diff --git a/dali-toolkit/internal/text/rendering/text-typesetter.h b/dali-toolkit/internal/text/rendering/text-typesetter.h index c1408cc..285348e 100644 --- a/dali-toolkit/internal/text/rendering/text-typesetter.h +++ b/dali-toolkit/internal/text/rendering/text-typesetter.h @@ -21,6 +21,7 @@ // EXTERNAL INCLUDES #include #include +#include #include #include #include @@ -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: diff --git a/dali-toolkit/internal/text/rendering/view-model.cpp b/dali-toolkit/internal/text/rendering/view-model.cpp index 3a0d319..1dcda36 100644 --- a/dali-toolkit/internal/text/rendering/view-model.cpp +++ b/dali-toolkit/internal/text/rendering/view-model.cpp @@ -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() ) diff --git a/dali-toolkit/internal/text/rendering/view-model.h b/dali-toolkit/internal/text/rendering/view-model.h index df0e32c..e15fa9c 100644 --- a/dali-toolkit/internal/text/rendering/view-model.h +++ b/dali-toolkit/internal/text/rendering/view-model.h @@ -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; diff --git a/dali-toolkit/internal/text/text-model-interface.h b/dali-toolkit/internal/text/text-model-interface.h index d84b46a..38e1fa2 100644 --- a/dali-toolkit/internal/text/text-model-interface.h +++ b/dali-toolkit/internal/text/text-model-interface.h @@ -24,6 +24,7 @@ // INTERNAL INCLUDES #include #include +#include #include 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. diff --git a/dali-toolkit/internal/text/text-model.cpp b/dali-toolkit/internal/text/text-model.cpp index 05cb405..2c00cf8 100644 --- a/dali-toolkit/internal/text/text-model.cpp +++ b/dali-toolkit/internal/text/text-model.cpp @@ -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(); diff --git a/dali-toolkit/internal/text/text-model.h b/dali-toolkit/internal/text/text-model.h index d8b08e9..a7b0bb8 100644 --- a/dali-toolkit/internal/text/text-model.h +++ b/dali-toolkit/internal/text/text-model.h @@ -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; diff --git a/dali-toolkit/internal/visuals/text/text-visual.cpp b/dali-toolkit/internal/visuals/text/text-visual.cpp index 46cb2a1..2968f07 100644 --- a/dali-toolkit/internal/visuals/text/text-visual.cpp +++ b/dali-toolkit/internal/visuals/text/text-visual.cpp @@ -20,6 +20,7 @@ // EXTERNAL INCLUDES #include +#include // INTERNAL HEADER #include @@ -31,6 +32,7 @@ #include #include #include +#include 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( 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 diff --git a/dali-toolkit/internal/visuals/text/text-visual.h b/dali-toolkit/internal/visuals/text/text-visual.h index 2ed7bee..31353b9 100644 --- a/dali-toolkit/internal/visuals/text/text-visual.h +++ b/dali-toolkit/internal/visuals/text/text-visual.h @@ -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 diff --git a/dali-toolkit/internal/visuals/visual-factory-cache.h b/dali-toolkit/internal/visuals/visual-factory-cache.h index cc68e0f..17d2246 100644 --- a/dali-toolkit/internal/visuals/visual-factory-cache.h +++ b/dali-toolkit/internal/visuals/visual-factory-cache.h @@ -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 }; -- 2.7.4