Add runMax argument to itemize function

The NDK font matching API only requires the single run result.
Not to perform itemization for unnecessary text region, add runMax
argument to terminate the itemization with certain count of runs.

Bug: 130044291
Test: minikin_tests
Change-Id: I660dfb928d8c69b3e639f22d6319cb293587a9e1
diff --git a/include/minikin/FontCollection.h b/include/minikin/FontCollection.h
index 86c008b..f136384 100644
--- a/include/minikin/FontCollection.h
+++ b/include/minikin/FontCollection.h
@@ -41,8 +41,15 @@
         int end;
     };
 
+    // Perform the itemization until given max runs.
     std::vector<Run> itemize(U16StringPiece text, FontStyle style, uint32_t localeListId,
-                             FamilyVariant familyVariant) const;
+                             FamilyVariant familyVariant, uint32_t runMax) const;
+
+    // Perform the itemization until end of the text.
+    std::vector<Run> itemize(U16StringPiece text, FontStyle style, uint32_t localeListId,
+                             FamilyVariant familyVariant) const {
+        return itemize(text, style, localeListId, familyVariant, text.size());
+    }
 
     // Returns true if there is a glyph for the code point and variation selector pair.
     // Returns false if no fonts have a glyph for the code point and variation
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp
index 39ceca8..6cbabea 100644
--- a/libs/minikin/FontCollection.cpp
+++ b/libs/minikin/FontCollection.cpp
@@ -370,7 +370,8 @@
 
 std::vector<FontCollection::Run> FontCollection::itemize(U16StringPiece text, FontStyle style,
                                                          uint32_t localeListId,
-                                                         FamilyVariant familyVariant) const {
+                                                         FamilyVariant familyVariant,
+                                                         uint32_t runMax) const {
     const uint16_t* string = text.data();
     const uint32_t string_size = text.size();
     std::vector<Run> result;
@@ -453,6 +454,15 @@
         if (run != nullptr) {
             run->end = nextUtf16Pos;  // exclusive
         }
+
+        // Stop searching the remaining characters if the result length gets runMax + 2.
+        // When result.size gets runMax + 2 here, the run between [0, runMax) was finalized.
+        // If the result.size() equals to runMax, the run may be still expanding.
+        // if the result.size() equals to runMax + 2, the last run may be removed and the last run
+        // may be exntended the previous run with above workaround.
+        if (result.size() >= 2 && runMax == result.size() - 2) {
+            break;
+        }
     } while (nextCh != kEndOfString);
 
     if (lastFamily == nullptr) {
@@ -460,6 +470,11 @@
         // getting displayed in. We put the whole string in one run, using the first font.
         result.push_back({mFamilies[0]->getClosestMatch(style), 0, static_cast<int>(string_size)});
     }
+
+    if (result.size() > runMax) {
+        // The itemization has terminated since it reaches the runMax. Remove last unfinalized runs.
+        result.resize(runMax);
+    }
     return result;
 }
 
diff --git a/tests/unittest/FontCollectionItemizeTest.cpp b/tests/unittest/FontCollectionItemizeTest.cpp
index 539b9eb..8cd95aa 100644
--- a/tests/unittest/FontCollectionItemizeTest.cpp
+++ b/tests/unittest/FontCollectionItemizeTest.cpp
@@ -65,8 +65,21 @@
 
     ParseUnicode(buf, BUF_SIZE, str, &len, NULL);
     const uint32_t localeListId = registerLocaleList(localeList);
-    return collection->itemize(U16StringPiece(buf, len), style, localeListId,
-                               FamilyVariant::DEFAULT);
+    auto result = collection->itemize(U16StringPiece(buf, len), style, localeListId,
+                                      FamilyVariant::DEFAULT);
+
+    // Check the same result has returned by calling with maxRun.
+    for (uint32_t runMax = 1; runMax <= result.size(); runMax++) {
+        auto resultWithRunMax = collection->itemize(U16StringPiece(buf, len), style, localeListId,
+                                                    FamilyVariant::DEFAULT, runMax);
+        EXPECT_EQ(runMax, resultWithRunMax.size());
+        for (uint32_t i = 0; i < runMax; ++i) {
+            EXPECT_EQ(result[i].start, resultWithRunMax[i].start);
+            EXPECT_EQ(result[i].end, resultWithRunMax[i].end);
+            EXPECT_EQ(result[i].fakedFont, resultWithRunMax[i].fakedFont);
+        }
+    }
+    return result;
 }
 
 // Overloaded version for default font style.