Reland "Reland "ICU: SkShaper (bidi iterator only)""

This reverts commit 03cde5f9997d47458c022c29e72b90d5933100f2.

Reason for revert: Fixing the build

Original change's description:
> Revert "Reland "ICU: SkShaper (bidi iterator only)""
>
> This reverts commit 7bc5542a9e752d87a7d65fbcb8e787798869789c.
>
> Reason for revert: breaking Android roll
>
> Original change's description:
> > Reland "ICU: SkShaper (bidi iterator only)"
> >
> > This reverts commit 73f003acfdc68bd7cc1aa1384442bd31d7da43fd.
> >
> > Reason for revert: Fixed the build
> >
> > Original change's description:
> > > Revert "ICU: SkShaper (bidi iterator only)"
> > >
> > > This reverts commit 64e3d040e911687a9e65514696c421a50953a6fe.
> > >
> > > Reason for revert: Breaking google3
> > >
> > > Original change's description:
> > > > ICU: SkShaper (bidi iterator only)
> > > >
> > > > Change-Id: I845cc0a962790ce37600f943473f21f619ee029b
> > > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/308508
> > > > Reviewed-by: Ben Wagner <bungeman@google.com>
> > > > Commit-Queue: Julia Lavrova <jlavrova@google.com>
> > >
> > > TBR=djsollen@google.com,bungeman@google.com,reed@google.com,jlavrova@google.com
> > >
> > > Change-Id: Ib081d97f61e57f74bb9414b3973cca640f3b3929
> > > No-Presubmit: true
> > > No-Tree-Checks: true
> > > No-Try: true
> > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309442
> > > Reviewed-by: Julia Lavrova <jlavrova@google.com>
> > > Commit-Queue: Julia Lavrova <jlavrova@google.com>
> >
> > TBR=djsollen@google.com,bungeman@google.com,reed@google.com,jlavrova@google.com
> >
> > Change-Id: I2d9c79deaaac97d3e0eb9b39ec9d53815fdb1f59
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/310757
> > Reviewed-by: Ben Wagner <bungeman@google.com>
> > Commit-Queue: Julia Lavrova <jlavrova@google.com>
>
> TBR=bungeman@google.com,jlavrova@google.com
>
> Change-Id: I89e80462edf8e014d7ea273885feeca17748a63a
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/313378
> Reviewed-by: Derek Sollenberger <djsollen@google.com>
> Commit-Queue: Derek Sollenberger <djsollen@google.com>

TBR=djsollen@google.com,bungeman@google.com,jlavrova@google.com

Change-Id: Idce514c64309e7463fa23425d10f1b13dd9c8d18
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/313407
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
diff --git a/modules/skshaper/src/SkUnicode_icu.cpp b/modules/skshaper/src/SkUnicode_icu.cpp
index 4e426c2..1a859fe 100644
--- a/modules/skshaper/src/SkUnicode_icu.cpp
+++ b/modules/skshaper/src/SkUnicode_icu.cpp
@@ -15,7 +15,7 @@
 #include <vector>
 #include <functional>
 
-using ICUBiDi = std::unique_ptr<UBiDi, SkFunctionWrapper<decltype(ubidi_close), ubidi_close>>;
+using SkUnicodeBidi = std::unique_ptr<UBiDi, SkFunctionWrapper<decltype(ubidi_close), ubidi_close>>;
 using ICUUText = std::unique_ptr<UText, SkFunctionWrapper<decltype(utext_close), utext_close>>;
 using ICUBreakIterator = std::unique_ptr<UBreakIterator, SkFunctionWrapper<decltype(ubrk_close), ubrk_close>>;
 
@@ -25,7 +25,92 @@
     return val < 0 ? 0xFFFD : val;
 }
 
-namespace skia {
+class SkBidiIterator_icu : public SkBidiIterator {
+    SkUnicodeBidi fBidi;
+public:
+    explicit SkBidiIterator_icu(SkUnicodeBidi bidi) : fBidi(std::move(bidi)) {}
+    Position getLength() override { return ubidi_getLength(fBidi.get()); }
+    Level getLevelAt(Position pos) override { return ubidi_getLevelAt(fBidi.get(), pos); }
+
+    static std::unique_ptr<SkBidiIterator> makeBidiIterator(const uint16_t utf16[], int utf16Units, Direction dir) {
+        UErrorCode status = U_ZERO_ERROR;
+        SkUnicodeBidi bidi(ubidi_openSized(utf16Units, 0, &status));
+        if (U_FAILURE(status)) {
+            SkDEBUGF("Bidi error: %s", u_errorName(status));
+            return nullptr;
+        }
+        SkASSERT(bidi);
+        uint8_t bidiLevel = (dir == SkBidiIterator::kLTR) ? UBIDI_LTR : UBIDI_RTL;
+        // The required lifetime of utf16 isn't well documented.
+        // It appears it isn't used after ubidi_setPara except through ubidi_getText.
+        ubidi_setPara(bidi.get(), (const UChar*)utf16, utf16Units, bidiLevel, nullptr, &status);
+        if (U_FAILURE(status)) {
+            SkDEBUGF("Bidi error: %s", u_errorName(status));
+            return nullptr;
+        }
+        return std::unique_ptr<SkBidiIterator>(new SkBidiIterator_icu(std::move(bidi)));
+    }
+
+    // ICU bidi iterator works with utf16 but clients (Flutter for instance) may work with utf8
+    // This method allows the clients not to think about all these details
+    static std::unique_ptr<SkBidiIterator> makeBidiIterator(const char utf8[], int utf8Units, Direction dir) {
+        // Convert utf8 into utf16 since ubidi only accepts utf16
+        if (!SkTFitsIn<int32_t>(utf8Units)) {
+            SkDEBUGF("Bidi error: text too long");
+            return nullptr;
+        }
+
+        // Getting the length like this seems to always set U_BUFFER_OVERFLOW_ERROR
+        int utf16Units = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, utf8Units);
+        if (utf16Units < 0) {
+            SkDEBUGF("Bidi error: Invalid utf8 input");
+            return nullptr;
+        }
+        std::unique_ptr<uint16_t[]> utf16(new uint16_t[utf16Units]);
+        SkDEBUGCODE(int dstLen =) SkUTF::UTF8ToUTF16(utf16.get(), utf16Units, utf8, utf8Units);
+        SkASSERT(dstLen == utf16Units);
+
+        return makeBidiIterator(utf16.get(), utf16Units, dir);
+    }
+
+    // This method returns the final results only: a list of bidi regions
+    // (this is all SkParagraph really needs; SkShaper however uses the iterator itself)
+    static std::vector<Region> getBidiRegions(const char utf8[], int utf8Units, Direction dir) {
+
+        auto bidiIterator = makeBidiIterator(utf8, utf8Units, dir);
+        std::vector<Region> bidiRegions;
+        const char* start8 = utf8;
+        const char* end8 = utf8 + utf8Units;
+        SkBidiIterator::Level currentLevel = 0;
+
+        Position pos8 = 0;
+        Position pos16 = 0;
+        Position end16 = bidiIterator->getLength();
+        while (pos16 < end16) {
+            auto level = bidiIterator->getLevelAt(pos16);
+            if (pos16 == 0) {
+                currentLevel = level;
+            } else if (level != currentLevel) {
+                auto end = start8 - utf8;
+                bidiRegions.emplace_back(pos8, end, currentLevel);
+                currentLevel = level;
+                pos8 = end;
+            }
+            SkUnichar u = utf8_next(&start8, end8);
+            pos16 += SkUTF::ToUTF16(u);
+        }
+        auto end = start8 - utf8;
+        if (end != pos8) {
+            bidiRegions.emplace_back(pos8, end, currentLevel);
+        }
+        return bidiRegions;
+    }
+};
+
+void SkBidiIterator::ReorderVisual(const Level runLevels[], int levelsCount,
+                                   int32_t logicalFromVisual[]) {
+    ubidi_reorderVisual(runLevels, levelsCount, logicalFromVisual);
+}
 
 class SkUnicode_icu : public SkUnicode {
 
@@ -52,7 +137,7 @@
         return utf16Units;
     }
 
-    static bool extractBidi(const char utf8[], int utf8Units,  Direction dir, std::vector<BidiRegion>* bidiRegions) {
+    static bool extractBidi(const char utf8[], int utf8Units, TextDirection dir, std::vector<BidiRegion>* bidiRegions) {
 
         // Convert to UTF16 since for now bidi iterator only operates on utf16
         std::unique_ptr<uint16_t[]> utf16;
@@ -63,13 +148,13 @@
 
         // Create bidi iterator
         UErrorCode status = U_ZERO_ERROR;
-        ICUBiDi bidi(ubidi_openSized(utf16Units, 0, &status));
+        SkUnicodeBidi bidi(ubidi_openSized(utf16Units, 0, &status));
         if (U_FAILURE(status)) {
             SkDEBUGF("Bidi error: %s", u_errorName(status));
             return false;
         }
         SkASSERT(bidi);
-        uint8_t bidiLevel = (dir == Direction::kLTR) ? UBIDI_LTR : UBIDI_RTL;
+        uint8_t bidiLevel = (dir == TextDirection::kLTR) ? UBIDI_LTR : UBIDI_RTL;
         // The required lifetime of utf16 isn't well documented.
         // It appears it isn't used after ubidi_setPara except through ubidi_getText.
         ubidi_setPara(bidi.get(), (const UChar*)utf16.get(), utf16Units, bidiLevel, nullptr, &status);
@@ -193,8 +278,16 @@
 
 public:
     ~SkUnicode_icu() override { }
+    std::unique_ptr<SkBidiIterator> makeBidiIterator(const uint16_t text[], int count,
+                                                     SkBidiIterator::Direction dir) override {
+        return SkBidiIterator_icu::makeBidiIterator(text, count, dir);
+    }
+    std::unique_ptr<SkBidiIterator> makeBidiIterator(const char text[], int count,
+                                                     SkBidiIterator::Direction dir) override {
+        return SkBidiIterator_icu::makeBidiIterator(text, count, dir);
+    }
 
-    bool getBidiRegions(const char utf8[], int utf8Units, Direction dir, std::vector<BidiRegion>* results) override {
+    bool getBidiRegions(const char utf8[], int utf8Units, TextDirection dir, std::vector<BidiRegion>* results) override {
         return extractBidi(utf8, utf8Units, dir, results);
     }
 
@@ -238,6 +331,3 @@
 };
 
 std::unique_ptr<SkUnicode> SkUnicode::Make() { return std::make_unique<SkUnicode_icu>(); }
-
-}  // namespace skia
-