Fix broken text rendering issue with emoji
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / text / text-visual.cpp
index 2106745..33e2c41 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/image-loading.h>
-#include <dali/devel-api/images/pixel-data-devel.h>
 #include <dali/devel-api/rendering/renderer-devel.h>
+#include <dali/devel-api/rendering/texture-devel.h>
 #include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
 #include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
 #include <string.h>
 
 // INTERNAL HEADER
@@ -50,9 +51,9 @@ namespace Internal
 {
 namespace
 {
-const int CUSTOM_PROPERTY_COUNT(5); // anim,premul,size,offset,multicol
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
 
-const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
+const int CUSTOM_PROPERTY_COUNT(2); // uTextColorAnimatable, uHasMultipleTextColors
 
 /**
  * Return Property index for the given string key
@@ -255,6 +256,7 @@ TextVisual::TextVisual(VisualFactoryCache& factoryCache, TextVisualShaderFactory
   mTypesetter(Text::Typesetter::New(mController->GetTextModel())),
   mTextVisualShaderFactory(shaderFactory),
   mTextShaderFeatureCache(),
+  mHasMultipleTextColorsIndex(Property::INVALID_INDEX),
   mAnimatableTextColorPropertyIndex(Property::INVALID_INDEX),
   mTextColorAnimatableIndex(Property::INVALID_INDEX),
   mRendererUpdateNeeded(false)
@@ -274,6 +276,7 @@ void TextVisual::OnInitialize()
 
   mImpl->mRenderer = VisualRenderer::New(geometry, shader);
   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
+  mHasMultipleTextColorsIndex = mImpl->mRenderer.RegisterUniqueProperty("uHasMultipleTextColors", static_cast<float>(false));
 }
 
 void TextVisual::DoSetProperties(const Property::Map& propertyMap)
@@ -503,10 +506,9 @@ void TextVisual::UpdateRenderer()
   relayoutSize.width  = floorf(0.5f + (isWidthRelative ? mImpl->mControlSize.width * mImpl->mTransform.mSize.x : mImpl->mTransform.mSize.width));
   relayoutSize.height = floorf(0.5f + (isHeightRelative ? mImpl->mControlSize.height * mImpl->mTransform.mSize.y : mImpl->mTransform.mSize.height));
 
-  std::string text;
-  mController->GetText(text);
+  auto textLengthUtf32 = mController->GetNumberOfCharacters();
 
-  if((fabsf(relayoutSize.width) < Math::MACHINE_EPSILON_1000) || (fabsf(relayoutSize.height) < Math::MACHINE_EPSILON_1000) || text.empty())
+  if((fabsf(relayoutSize.width) < Math::MACHINE_EPSILON_1000) || (fabsf(relayoutSize.height) < Math::MACHINE_EPSILON_1000) || textLengthUtf32 == 0u)
   {
     // Remove the texture set and any renderer previously set.
     RemoveRenderer(control);
@@ -562,14 +564,16 @@ void TextVisual::UpdateRenderer()
         shadowEnabled = true;
       }
 
-      const bool underlineEnabled       = mController->GetTextModel()->IsUnderlineEnabled();
-      const bool outlineEnabled         = (mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1);
-      const bool backgroundEnabled      = mController->GetTextModel()->IsBackgroundEnabled();
-      const bool markupProcessorEnabled = mController->IsMarkupProcessorEnabled();
-      const bool strikethroughEnabled   = mController->GetTextModel()->IsStrikethroughEnabled();
-
-      const bool styleEnabled   = (shadowEnabled || underlineEnabled || outlineEnabled || backgroundEnabled || markupProcessorEnabled || strikethroughEnabled);
-      const bool isOverlayStyle = underlineEnabled || strikethroughEnabled;
+      const bool outlineEnabled             = (mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1);
+      const bool backgroundEnabled          = mController->GetTextModel()->IsBackgroundEnabled();
+      const bool markupOrSpannedText        = mController->IsMarkupProcessorEnabled() || mController->GetTextModel()->IsSpannedTextPlaced();
+      const bool markupUnderlineEnabled     = markupOrSpannedText && mController->GetTextModel()->IsMarkupUnderlineSet();
+      const bool markupStrikethroughEnabled = markupOrSpannedText && mController->GetTextModel()->IsMarkupStrikethroughSet();
+      const bool underlineEnabled           = mController->GetTextModel()->IsUnderlineEnabled() || markupUnderlineEnabled;
+      const bool strikethroughEnabled       = mController->GetTextModel()->IsStrikethroughEnabled() || markupStrikethroughEnabled;
+      const bool backgroundMarkupSet        = mController->GetTextModel()->IsMarkupBackgroundColorSet();
+      const bool styleEnabled               = (shadowEnabled || outlineEnabled || backgroundEnabled || markupOrSpannedText || backgroundMarkupSet);
+      const bool isOverlayStyle             = underlineEnabled || strikethroughEnabled;
 
       AddRenderer(control, relayoutSize, hasMultipleTextColors, containsColorGlyph, styleEnabled, isOverlayStyle);
 
@@ -591,53 +595,47 @@ void TextVisual::AddTexture(TextureSet& textureSet, PixelData& data, Sampler& sa
   textureSet.SetSampler(textureSetIndex, sampler);
 }
 
-PixelData TextVisual::ConvertToPixelData(unsigned char* buffer, int width, int height, int offsetPosition, const Pixel::Format textPixelFormat)
+void TextVisual::AddTilingTexture(TextureSet& textureSet, TilingInfo& tilingInfo, PixelData& data, Sampler& sampler, unsigned int textureSetIndex)
 {
-  int            bpp        = Pixel::GetBytesPerPixel(textPixelFormat);
-  unsigned int   bufferSize = width * height * bpp;
-  unsigned char* dstBuffer  = static_cast<unsigned char*>(malloc(bufferSize));
-  memcpy(dstBuffer, buffer + offsetPosition * bpp, bufferSize);
-  PixelData pixelData = Dali::PixelData::New(dstBuffer,
-                                             bufferSize,
-                                             width,
-                                             height,
-                                             textPixelFormat,
-                                             Dali::PixelData::FREE);
-  return pixelData;
+  Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D,
+                                 tilingInfo.textPixelFormat,
+                                 tilingInfo.width,
+                                 tilingInfo.height);
+  DevelTexture::UploadSubPixelData(texture, data, 0u, tilingInfo.offsetHeight, tilingInfo.width, tilingInfo.height);
+
+  textureSet.SetTexture(textureSetIndex, texture);
+  textureSet.SetSampler(textureSetIndex, sampler);
 }
 
 void TextVisual::CreateTextureSet(TilingInfo& info, VisualRenderer& renderer, Sampler& sampler)
 {
-  TextureSet   textureSet      = TextureSet::New();
-  unsigned int textureSetIndex = 0u;
+  TextureSet textureSet      = TextureSet::New();
+  uint32_t   textureSetIndex = 0u;
 
   // Convert the buffer to pixel data to make it a texture.
 
-  if(info.textBuffer)
+  if(info.textPixelData)
   {
-    PixelData data = ConvertToPixelData(info.textBuffer, info.width, info.height, info.offsetPosition, info.textPixelFormat);
-    AddTexture(textureSet, data, sampler, textureSetIndex);
+    AddTilingTexture(textureSet, info, info.textPixelData, sampler, textureSetIndex);
     ++textureSetIndex;
   }
 
-  if(mTextShaderFeatureCache.IsEnabledStyle() && info.styleBuffer)
+  if(mTextShaderFeatureCache.IsEnabledStyle() && info.stylePixelData)
   {
-    PixelData styleData = ConvertToPixelData(info.styleBuffer, info.width, info.height, info.offsetPosition, Pixel::RGBA8888);
-    AddTexture(textureSet, styleData, sampler, textureSetIndex);
+    AddTilingTexture(textureSet, info, info.stylePixelData, sampler, textureSetIndex);
     ++textureSetIndex;
   }
 
-  if(mTextShaderFeatureCache.IsEnabledOverlay() && info.overlayStyleBuffer)
+  if(mTextShaderFeatureCache.IsEnabledOverlay() && info.overlayStylePixelData)
   {
-    PixelData overlayStyleData = ConvertToPixelData(info.overlayStyleBuffer, info.width, info.height, info.offsetPosition, Pixel::RGBA8888);
-    AddTexture(textureSet, overlayStyleData, sampler, textureSetIndex);
+    AddTilingTexture(textureSet, info, info.overlayStylePixelData, sampler, textureSetIndex);
     ++textureSetIndex;
   }
 
-  if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor() && info.maskBuffer)
+  if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor() && info.maskPixelData)
   {
-    PixelData maskData = ConvertToPixelData(info.maskBuffer, info.width, info.height, info.offsetPosition, Pixel::L8);
-    AddTexture(textureSet, maskData, sampler, textureSetIndex);
+    AddTilingTexture(textureSet, info, info.maskPixelData, sampler, textureSetIndex);
+    ++textureSetIndex;
   }
 
   renderer.SetTextures(textureSet);
@@ -647,11 +645,11 @@ void TextVisual::CreateTextureSet(TilingInfo& info, VisualRenderer& renderer, Sa
 
   // Enable the pre-multiplied alpha to improve the text quality
   renderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true);
-  renderer.RegisterProperty(PREMULTIPLIED_ALPHA, 1.0f);
+  renderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, true);
 
   // Set size and offset for the tiling.
   renderer.SetProperty(VisualRenderer::Property::TRANSFORM_SIZE, Vector2(info.width, info.height));
-  renderer.SetProperty(VisualRenderer::Property::TRANSFORM_OFFSET, Vector2(info.offSet.x, info.offSet.y));
+  renderer.SetProperty(VisualRenderer::Property::TRANSFORM_OFFSET, info.transformOffset);
   renderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
   renderer.RegisterProperty("uHasMultipleTextColors", static_cast<float>(mTextShaderFeatureCache.IsEnabledMultiColor()));
 
@@ -663,6 +661,8 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
   Shader shader = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder().EnableMultiColor(hasMultipleTextColors).EnableEmoji(containsColorGlyph).EnableStyle(styleEnabled).EnableOverlay(isOverlayStyle));
   mImpl->mRenderer.SetShader(shader);
 
+  DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_VISUAL_UPDATE_RENDERER");
+
   // Get the maximum size.
   const int maxTextureSize = Dali::GetMaxTextureSize();
 
@@ -674,7 +674,7 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     mImpl->mRenderer.SetTextures(textureSet);
     //Register transform properties
     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
-    mImpl->mRenderer.RegisterProperty("uHasMultipleTextColors", static_cast<float>(hasMultipleTextColors));
+    mImpl->mRenderer.SetProperty(mHasMultipleTextColorsIndex, static_cast<float>(hasMultipleTextColors));
     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
 
     mRendererList.push_back(mImpl->mRenderer);
@@ -701,32 +701,25 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     // Set information for creating textures.
     TilingInfo info(verifiedWidth, maxTextureSize, textPixelFormat);
 
-    // Get the buffer of text.
-    Dali::DevelPixelData::PixelDataBuffer textPixelData = Dali::DevelPixelData::ReleasePixelDataBuffer(data);
-    info.textBuffer                                     = textPixelData.buffer;
+    // Get the pixel data of text.
+    info.textPixelData = data;
 
     if(mTextShaderFeatureCache.IsEnabledStyle())
     {
       // Create RGBA texture for all the text styles (without the text itself)
-      PixelData                             styleData      = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888);
-      Dali::DevelPixelData::PixelDataBuffer stylePixelData = Dali::DevelPixelData::ReleasePixelDataBuffer(styleData);
-      info.styleBuffer                                     = stylePixelData.buffer;
+      info.stylePixelData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888);
     }
 
     if(mTextShaderFeatureCache.IsEnabledOverlay())
     {
       // Create RGBA texture for all the overlay styles
-      PixelData                             overlayStyleData      = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_OVERLAY_STYLE, false, Pixel::RGBA8888);
-      Dali::DevelPixelData::PixelDataBuffer overlayStylePixelData = Dali::DevelPixelData::ReleasePixelDataBuffer(overlayStyleData);
-      info.overlayStyleBuffer                                     = overlayStylePixelData.buffer;
+      info.overlayStylePixelData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_OVERLAY_STYLE, false, Pixel::RGBA8888);
     }
 
     if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor())
     {
       // Create a L8 texture as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
-      PixelData                             maskData      = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8);
-      Dali::DevelPixelData::PixelDataBuffer maskPixelData = Dali::DevelPixelData::ReleasePixelDataBuffer(maskData);
-      info.maskBuffer                                     = maskPixelData.buffer;
+      info.maskPixelData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8);
     }
 
     // Get the current offset for recalculate the offset when tiling.
@@ -735,7 +728,7 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     Property::Value* offsetValue = retMap.Find(Dali::Toolkit::Visual::Transform::Property::OFFSET);
     if(offsetValue)
     {
-      offsetValue->Get(info.offSet);
+      offsetValue->Get(info.transformOffset);
     }
 
     // Create a textureset in the default renderer.
@@ -745,18 +738,17 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
 
     Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
 
-    int offsetPosition = verifiedWidth * maxTextureSize;
     // Create a renderer by cutting maxTextureSize.
     while(verifiedHeight > 0)
     {
       VisualRenderer tilingRenderer = VisualRenderer::New(geometry, shader);
       tilingRenderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT);
       // New offset position of buffer for tiling.
-      info.offsetPosition += offsetPosition;
+      info.offsetHeight += maxTextureSize;
       // New height for tiling.
       info.height = (verifiedHeight - maxTextureSize) > 0 ? maxTextureSize : verifiedHeight;
       // New offset for tiling.
-      info.offSet.y += maxTextureSize;
+      info.transformOffset.y += maxTextureSize;
       // Create a textureset int the new tiling renderer.
       CreateTextureSet(info, tilingRenderer, sampler);
 
@@ -819,9 +811,7 @@ TextureSet TextVisual::GetTextTexture(const Vector2& size)
   // Create a texture for the text without any styles
   PixelData data = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat);
 
-  // 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.
-  unsigned int textureSetIndex = 0u;
+  uint32_t textureSetIndex = 0u;
   AddTexture(textureSet, data, sampler, textureSetIndex);
   ++textureSetIndex;
 
@@ -858,7 +848,6 @@ Shader TextVisual::GetTextShader(VisualFactoryCache& factoryCache, const TextVis
   mTextShaderFeatureCache = featureBuilder;
 
   Shader shader = mTextVisualShaderFactory.GetShader(factoryCache, mTextShaderFeatureCache);
-  shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
   return shader;
 }