[dali_2.3.42] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / text-typesetter.cpp
index 7d095b4..503b394 100644 (file)
@@ -19,7 +19,9 @@
 #include <dali-toolkit/internal/text/rendering/text-typesetter.h>
 
 // EXTERNAL INCLUDES
+#include <cmath>
 #include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/integration-api/debug.h>
 #include <dali/integration-api/trace.h>
 #include <dali/public-api/common/constants.h>
 #include <dali/public-api/math/math-utils.h>
@@ -60,6 +62,23 @@ inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexc
   return ((xy << 15) + (xy << 7) + xy) >> 23;
 }
 
+/**
+ * @brief Fast multiply & Summation & divide by 255.
+ *
+ * @param x1 The value between [0..255]
+ * @param y1 The value between [0..255]
+ * @param x2 The value between [0..255]
+ * @param y2 The value between [0..255]
+ * @return min(255, (x1*y1)/255 + (x2*y2)/255)
+ */
+inline uint8_t MultiplyAndSummationAndNormalizeColor(const uint8_t x1, const uint8_t y1, const uint8_t x2, const uint8_t y2) noexcept
+{
+  const uint32_t xy1 = static_cast<const uint32_t>(x1) * y1;
+  const uint32_t xy2 = static_cast<const uint32_t>(x2) * y2;
+  const uint32_t res = std::min(65025u, xy1 + xy2); // 65025 is 255 * 255.
+  return ((res + ((res + 257) >> 8)) >> 8); // fast divide by 255.
+}
+
 /// Helper macro define for glyph typesetter. It will reduce some duplicated code line.
 // clang-format off
 /**
@@ -584,6 +603,8 @@ Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuff
   const Vector2* const    positionBuffer               = model->GetLayout();
   const Vector4* const    backgroundColorsBuffer       = model->GetBackgroundColors();
   const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
+  const bool              removeFrontInset             = model->IsRemoveFrontInset();
+  const bool              removeBackInset              = model->IsRemoveBackInset();
 
   const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
 
@@ -653,14 +674,36 @@ Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuff
       }
 
       // Calculate the positions of leftmost and rightmost glyphs in the current line
-      if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
+      if(removeFrontInset)
       {
-        left = position->x - glyphInfo->xBearing;
+        if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
+        {
+          left = position->x;
+        }
+      }
+      else
+      {
+        const float originPositionLeft = position->x - glyphInfo->xBearing;
+        if((originPositionLeft < left) || (backgroundColorIndex != prevBackgroundColorIndex))
+        {
+          left = originPositionLeft;
+        }
       }
 
-      if(position->x + glyphInfo->width > right)
+      if(removeBackInset)
       {
-        right = position->x - glyphInfo->xBearing + glyphInfo->advance;
+        if(position->x + glyphInfo->width > right)
+        {
+          right = position->x + glyphInfo->width;
+        }
+      }
+      else
+      {
+        const float originPositionRight = position->x - glyphInfo->xBearing + glyphInfo->advance;
+        if(originPositionRight > right)
+        {
+          right = originPositionRight;
+        }
       }
 
       prevBackgroundColorIndex = backgroundColorIndex;
@@ -894,22 +937,43 @@ ViewModel* Typesetter::GetViewModel()
   return mModel;
 }
 
+void Typesetter::SetFontClient(TextAbstraction::FontClient& fontClient)
+{
+  mFontClient = fontClient;
+}
+
 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
 {
+  Devel::PixelBuffer result = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
+  PixelData pixelData = Devel::PixelBuffer::Convert(result);
+
+  return pixelData;
+}
+
+PixelData Typesetter::RenderWithCutout(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, Devel::PixelBuffer mask, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, float originAlpha)
+{
+  Devel::PixelBuffer result = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
+  SetMaskForImageBuffer(mask, result, size.width, size.height, originAlpha);
+
+  PixelData pixelData = Devel::PixelBuffer::Convert(result);
+
+  return pixelData;
+}
+
+Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
+{
   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_RENDERING_TYPESETTER");
   // @todo. This initial implementation for a TextLabel has only one visible page.
 
   // Elides the text if needed.
-  mModel->ElideGlyphs();
+  mModel->ElideGlyphs(mFontClient);
 
   // Retrieves the layout size.
   const Size& layoutSize = mModel->GetLayoutSize();
-
   const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
 
   // Set the offset for the horizontal alignment according to the text direction and outline width.
   int32_t penX = 0;
-
   switch(mModel->GetHorizontalAlignment())
   {
     case HorizontalAlignment::BEGIN:
@@ -931,7 +995,6 @@ PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirect
 
   // Set the offset for the vertical alignment.
   int32_t penY = 0u;
-
   switch(mModel->GetVerticalAlignment())
   {
     case VerticalAlignment::TOP:
@@ -941,8 +1004,9 @@ PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirect
     }
     case VerticalAlignment::CENTER:
     {
-      penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
+      penY = static_cast<int32_t>(std::round(0.5f * (size.height - layoutSize.height)));
       penY = penY < 0.f ? 0.f : penY;
+
       break;
     }
     case VerticalAlignment::BOTTOM:
@@ -952,6 +1016,14 @@ PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirect
     }
   }
 
+  const bool isCutoutEnabled = mModel->IsCutoutEnabled();
+  if(isCutoutEnabled)
+  {
+    Vector2 offset = mModel->GetOffsetWithCutout();
+    penX = offset.x;
+    penY = offset.y;
+  }
+
   // Generate the image buffers of the text for each different style first,
   // then combine all of them together as one final image buffer. We try to
   // do all of these in CPU only, so that once the final texture is generated,
@@ -1054,6 +1126,18 @@ PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirect
       CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
     }
 
+    // Generate the background_with_mask if enabled
+    const bool backgroundWithCutoutEnabled   = mModel->IsBackgroundWithCutoutEnabled();
+    if((backgroundWithCutoutEnabled) && RENDER_OVERLAY_STYLE != behaviour)
+    {
+      Devel::PixelBuffer backgroundImageBuffer;
+
+      backgroundImageBuffer = CreateFullBackgroundBuffer(bufferWidth, bufferHeight, mModel->GetBackgroundColorWithCutout());
+
+      // Combine the two buffers
+      CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
+    }
+
     if(RENDER_OVERLAY_STYLE == behaviour)
     {
       if(mModel->IsUnderlineEnabled())
@@ -1090,10 +1174,30 @@ PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirect
     }
   }
 
-  // Create the final PixelData for the combined image buffer
-  PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
+  return imageBuffer;
+}
 
-  return pixelData;
+Devel::PixelBuffer Typesetter::CreateFullBackgroundBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Vector4& backgroundColor)
+{
+  const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
+  uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
+
+  Devel::PixelBuffer buffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
+
+  uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(buffer.GetBuffer());
+
+  uint32_t packedBackgroundColor       = 0u;
+  uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
+
+  // Write the color to the pixel buffer
+  *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
+  *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
+  *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
+  *(packedBackgroundColorBuffer)      = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
+
+  std::fill(bitmapBuffer, bitmapBuffer + bufferSizeInt, packedBackgroundColor);
+
+  return buffer;
 }
 
 Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Typesetter::Style style, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset, const GlyphIndex fromGlyphIndex, const GlyphIndex toGlyphIndex)
@@ -1108,6 +1212,9 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, con
   const GlyphInfo* __restrict__ hyphens                 = mModel->GetHyphens();
   const Length* __restrict__ hyphenIndices              = mModel->GetHyphenIndices();
   const Length hyphensCount                             = mModel->GetHyphensCount();
+  const bool removeFrontInset                           = mModel->IsRemoveFrontInset();
+  const bool removeBackInset                            = mModel->IsRemoveBackInset();
+  const bool cutoutEnabled                              = mModel->IsCutoutEnabled();
 
   // Elided text info. Indices according to elided text and Ellipsis position.
   const auto startIndexOfGlyphs              = mModel->GetStartIndexOfElidedGlyphs();
@@ -1129,7 +1236,6 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, con
   glyphData.horizontalOffset = 0;
 
   // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
-  TextAbstraction::FontClient fontClient  = TextAbstraction::FontClient::Get();
   Length                      hyphenIndex = 0;
 
   const Character* __restrict__ textBuffer                       = mModel->GetTextBuffer();
@@ -1299,7 +1405,7 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, con
       {
         // We need to fetch fresh font underline metrics
         FontMetrics fontMetrics;
-        fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
+        mFontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
 
         //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
         currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
@@ -1336,14 +1442,36 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, con
       }
 
       // Calculate the positions of leftmost and rightmost glyphs in the current line
-      if(position.x < lineExtentLeft)
+      if(removeFrontInset)
+      {
+        if(position.x < lineExtentLeft)
+        {
+          lineExtentLeft = position.x;
+        }
+      }
+      else
       {
-        lineExtentLeft = position.x;
+        const float originPositionLeft = position.x - glyphInfo->xBearing;
+        if(originPositionLeft < lineExtentLeft)
+        {
+          lineExtentLeft = originPositionLeft;
+        }
       }
 
-      if(position.x + glyphInfo->width > lineExtentRight)
+      if(removeBackInset)
       {
-        lineExtentRight = position.x + glyphInfo->width;
+        if(position.x + glyphInfo->width > lineExtentRight)
+        {
+          lineExtentRight = position.x + glyphInfo->width;
+        }
+      }
+      else
+      {
+        const float originPositionRight = position.x - glyphInfo->xBearing + glyphInfo->advance;
+        if(originPositionRight > lineExtentRight)
+        {
+          lineExtentRight = originPositionRight;
+        }
       }
 
       // Retrieves the glyph's color.
@@ -1363,6 +1491,12 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, con
         color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
       }
 
+      if(style == Typesetter::STYLE_NONE && cutoutEnabled)
+      {
+        // Temporarily adjust the transparency to 1.f
+        color.a = 1.f;
+      }
+
       // Premultiply alpha
       color.r *= color.a;
       color.g *= color.a;
@@ -1381,12 +1515,12 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, con
 
       if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
       {
-        fontClient.CreateBitmap(glyphInfo->fontId,
-                                glyphInfo->index,
-                                glyphInfo->isItalicRequired,
-                                glyphInfo->isBoldRequired,
-                                glyphData.glyphBitmap,
-                                static_cast<int32_t>(outlineWidth));
+        mFontClient.CreateBitmap(glyphInfo->fontId,
+                                 glyphInfo->index,
+                                 glyphInfo->isItalicRequired,
+                                 glyphInfo->isBoldRequired,
+                                 glyphData.glyphBitmap,
+                                 static_cast<int32_t>(outlineWidth));
       }
 
       // Sets the glyph's bitmap into the bitmap of the whole text.
@@ -1528,9 +1662,54 @@ Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelB
   return topPixelBuffer;
 }
 
+void Typesetter::SetMaskForImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, float originAlpha)
+{
+  // Assume that we always combine two RGBA images
+  // Jump with 4bytes for optimize runtime.
+  uint32_t* topBuffer    = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
+  uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
+
+  if(topBuffer == NULL || bottomBuffer == NULL)
+  {
+    // Nothing to do if one of both buffers are empty.
+    return;
+  }
+
+  const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
+
+  for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
+  {
+    uint32_t topBufferColor = *(topBuffer);
+    uint32_t bottomBufferColor = *(bottomBuffer);
+    uint8_t* __restrict__ topBufferColorBuffer = reinterpret_cast<uint8_t*>(&topBufferColor);
+    uint8_t* __restrict__ bottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&bottomBufferColor);
+
+    // Return the transparency of the text to original.
+    uint8_t originAlphaInt = originAlpha * 255;
+
+    uint8_t topAlpha = topBufferColorBuffer[3];
+    uint8_t bottomAlpha = 255 - topAlpha;
+
+    // Manual blending.
+    bottomBufferColorBuffer[0] = MultiplyAndSummationAndNormalizeColor(topBufferColorBuffer[0], originAlphaInt, bottomBufferColorBuffer[0], bottomAlpha);
+    bottomBufferColorBuffer[1] = MultiplyAndSummationAndNormalizeColor(topBufferColorBuffer[1], originAlphaInt, bottomBufferColorBuffer[1], bottomAlpha);
+    bottomBufferColorBuffer[2] = MultiplyAndSummationAndNormalizeColor(topBufferColorBuffer[2], originAlphaInt, bottomBufferColorBuffer[2], bottomAlpha);
+    bottomBufferColorBuffer[3] = MultiplyAndSummationAndNormalizeColor(topBufferColorBuffer[3], originAlphaInt, bottomBufferColorBuffer[3], bottomAlpha);
+
+    *(bottomBuffer) = bottomBufferColor;
+
+    // Increase each buffer's pointer.
+    ++topBuffer;
+    ++bottomBuffer;
+  }
+}
+
 Typesetter::Typesetter(const ModelInterface* const model)
-: mModel(new ViewModel(model))
+: mModel(new ViewModel(model)),
+  mFontClient()
 {
+  // Default font client set.
+  mFontClient = TextAbstraction::FontClient::Get();
 }
 
 Typesetter::~Typesetter()