TextFit check without candidate listup 58/284558/1
authorEunki, Hong <eunkiki.hong@samsung.com>
Fri, 18 Nov 2022 13:47:19 +0000 (22:47 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Fri, 18 Nov 2022 14:08:42 +0000 (23:08 +0900)
Previous code required O((maxPointSize - minPointSize) / pointInterval) memory.
Now we make to pick valid pointsize without listup.
Also, make binary search as overflow-safety.

Change-Id: I6542c1692e94395fdab07a91fd7322b718a510e1
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp
dali-toolkit/internal/text/controller/text-controller-relayouter.cpp

index 27a14a9..41783dc 100644 (file)
@@ -1508,7 +1508,6 @@ int UtcDaliToolkitTextlabelScrollingN(void)
   const bool enabled = label.GetProperty(TextLabel::Property::ENABLE_AUTO_SCROLL).Get<bool>();
   DALI_TEST_CHECK(!enabled);
 
-
   label.SetProperty(TextLabel::Property::MULTI_LINE, false);
   label.SetProperty(TextLabel::Property::AUTO_SCROLL_LOOP_COUNT, 1);
   label.SetProperty(TextLabel::Property::AUTO_SCROLL_SPEED, 9999.0f);
@@ -2038,6 +2037,47 @@ int UtcDaliToolkitTextlabelTextFit(void)
   END_TEST;
 }
 
+int UtcDaliToolkitTextlabelTextFitStressTest(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextlabelTextFitStressTest");
+  TextLabel label = TextLabel::New();
+  Vector2   size(460.0f, 100.0f);
+  label.SetProperty(Actor::Property::SIZE, size);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello world");
+
+  // connect to the text git changed signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::TextFitChangedSignal(label).Connect(&TestTextFitChangedCallback);
+  bool textFitChangedSignal = false;
+  label.ConnectSignal(testTracker, "textFitChanged", CallbackFunctor(&textFitChangedSignal));
+  gTextFitChangedCallBackCalled = false;
+
+  // check point size with veryvery big range
+  Property::Map textFitMapSet;
+  textFitMapSet["enable"]       = true;
+  textFitMapSet["minSize"]      = 10.f;
+  textFitMapSet["maxSize"]      = 10000.f;
+  textFitMapSet["stepSize"]     = -1.0f;
+  textFitMapSet["fontSizeType"] = "pointSize";
+
+  label.SetProperty(Toolkit::DevelTextLabel::Property::TEXT_FIT, textFitMapSet);
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 120.f);
+
+  application.GetScene().Add(label);
+
+  application.SendNotification();
+  application.Render();
+
+  const Vector3 EXPECTED_NATURAL_SIZE(450.0f, 96.0f, 0.0f);
+  DALI_TEST_EQUALS(EXPECTED_NATURAL_SIZE, label.GetNaturalSize(), TEST_LOCATION);
+
+  DALI_TEST_CHECK(gTextFitChangedCallBackCalled);
+  DALI_TEST_CHECK(textFitChangedSignal);
+
+  END_TEST;
+}
+
 int UtcDaliToolkitTextlabelMaxTextureSet(void)
 {
   ToolkitTestApplication application;
@@ -2138,7 +2178,6 @@ int UtcDaliToolkitTextlabelMaxTextureSet(void)
   application.SendNotification();
   application.Render();
 
-
   END_TEST;
 }
 
@@ -2687,7 +2726,6 @@ int utcDaliTextLabelGeometryOneGlyph(void)
   END_TEST;
 }
 
-
 int utcDaliTextLabelGeometryNullPtr(void)
 {
   ToolkitTestApplication application;
index c759051..f95019a 100644 (file)
@@ -23,9 +23,9 @@
 #include <limits>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/internal/text/layouts/layout-parameters.h>
 #include <dali-toolkit/internal/text/controller/text-controller-event-handler.h>
 #include <dali-toolkit/internal/text/controller/text-controller-impl.h>
+#include <dali-toolkit/internal/text/layouts/layout-parameters.h>
 
 namespace
 {
@@ -253,48 +253,65 @@ void Controller::Relayouter::FitPointSizeforLayout(Controller& controller, const
     float currentFitPointSize = impl.mFontDefaults->mFitPointSize;
 
     model->mElideEnabled = false;
-    Vector<float> pointSizeArray;
 
     // check zero value
     if(pointInterval < 1.f)
     {
       impl.mTextFitStepSize = pointInterval = 1.0f;
     }
+    uint32_t pointSizeRange = static_cast<uint32_t>(ceil((maxPointSize - minPointSize) / pointInterval));
 
-    pointSizeArray.Reserve(static_cast<unsigned int>(ceil((maxPointSize - minPointSize) / pointInterval)));
-
-    for(float i = minPointSize; i < maxPointSize; i += pointInterval)
+    // Ensure minPointSize + pointSizeRange * pointInverval >= maxPointSize
+    while(minPointSize + static_cast<float>(pointSizeRange) * pointInterval < maxPointSize)
     {
-      pointSizeArray.PushBack(i);
+      ++pointSizeRange;
     }
 
-    pointSizeArray.PushBack(maxPointSize);
-
-    int bestSizeIndex = 0;
-    int min           = bestSizeIndex + 1;
-    int max           = pointSizeArray.Size() - 1;
-    while(min <= max)
+    uint32_t bestSizeIndex = 0;
+    uint32_t minIndex      = bestSizeIndex + 1u;
+    uint32_t maxIndex      = pointSizeRange + 1u;
+
+    bool bestSizeUpdatedLatest = false;
+    // Find best size as binary search.
+    // Range format as [l r). (left closed, right opened)
+    // It mean, we already check all i < l is valid, and r <= i is invalid.
+    // Below binary search will check m = (l+r)/2 point.
+    // Search area sperate as [l m) or [m+1 r)
+    //
+    // Basically, we can assume that 0 (minPointSize) is always valid.
+    // Now, we will check [1 pointSizeRange] range s.t. pointSizeRange mean the maxPointSize
+    while(minIndex < maxIndex)
     {
-      int destI = (min + max) / 2;
+      uint32_t    testIndex     = minIndex + ((maxIndex - minIndex) >> 1u);
+      const float testPointSize = std::min(maxPointSize, minPointSize + static_cast<float>(testIndex) * pointInterval);
 
-      if(CheckForTextFit(controller, pointSizeArray[destI], layoutSize))
+      if(CheckForTextFit(controller, testPointSize, layoutSize))
       {
-        bestSizeIndex = min;
-        min           = destI + 1;
+        bestSizeUpdatedLatest = true;
+
+        bestSizeIndex = testIndex;
+        minIndex      = testIndex + 1u;
       }
       else
       {
-        max           = destI - 1;
-        bestSizeIndex = max;
+        bestSizeUpdatedLatest = false;
+        maxIndex              = testIndex;
       }
     }
+    const float bestPointSize = std::min(maxPointSize, minPointSize + static_cast<float>(bestSizeIndex) * pointInterval);
+
+    // Best point size was not updated. re-run so the TextFit should be fitted really.
+    if(!bestSizeUpdatedLatest)
+    {
+      CheckForTextFit(controller, bestPointSize, layoutSize);
+    }
 
     model->mElideEnabled = actualellipsis;
-    if(currentFitPointSize != pointSizeArray[bestSizeIndex])
+    if(currentFitPointSize != bestPointSize)
     {
       impl.mTextFitChanged = true;
     }
-    impl.mFontDefaults->mFitPointSize = pointSizeArray[bestSizeIndex];
+    impl.mFontDefaults->mFitPointSize = bestPointSize;
     impl.mFontDefaults->sizeDefined   = true;
     impl.ClearFontData();
   }