ICU project: script iterator in SkShaper

Change-Id: Idcc9290a7666cb590532150a44304d704c8ee34c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/319777
Commit-Queue: Julia Lavrova <jlavrova@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
diff --git a/modules/skshaper/src/SkShaper.cpp b/modules/skshaper/src/SkShaper.cpp
index 185d349..c47e3ad 100644
--- a/modules/skshaper/src/SkShaper.cpp
+++ b/modules/skshaper/src/SkShaper.cpp
@@ -40,6 +40,9 @@
 SkShaper::MakeBiDiRunIterator(const char* utf8, size_t utf8Bytes, uint8_t bidiLevel) {
 #ifdef SK_UNICODE_AVAILABLE
     auto unicode = SkUnicode::Make();
+    if (!unicode) {
+        return nullptr;
+    }
     std::unique_ptr<SkShaper::BiDiRunIterator> bidi =
         SkShaper::MakeSkUnicodeBidiRunIterator(unicode.get(),
                                                utf8,
@@ -54,9 +57,13 @@
 
 std::unique_ptr<SkShaper::ScriptRunIterator>
 SkShaper::MakeScriptRunIterator(const char* utf8, size_t utf8Bytes, SkFourByteTag scriptTag) {
-#ifdef SK_SHAPER_HARFBUZZ_AVAILABLE
+#if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && defined(SK_UNICODE_AVAILABLE)
+    auto unicode = SkUnicode::Make();
+    if (!unicode) {
+        return nullptr;
+    }
     std::unique_ptr<SkShaper::ScriptRunIterator> script =
-        SkShaper::MakeHbIcuScriptRunIterator(utf8, utf8Bytes);
+        SkShaper::MakeSkUnicodeHbScriptRunIterator(unicode.get(), utf8, utf8Bytes);
     if (script) {
         return script;
     }
diff --git a/modules/skshaper/src/SkShaper_harfbuzz.cpp b/modules/skshaper/src/SkShaper_harfbuzz.cpp
index 293924e..e6ed1b0 100644
--- a/modules/skshaper/src/SkShaper_harfbuzz.cpp
+++ b/modules/skshaper/src/SkShaper_harfbuzz.cpp
@@ -63,6 +63,7 @@
 
 using SkUnicodeBidi = std::unique_ptr<SkBidiIterator>;
 using SkUnicodeBreak = std::unique_ptr<SkBreakIterator>;
+using SkUnicodeScript = std::unique_ptr<SkScriptIterator>;
 
 hb_position_t skhb_position(SkScalar value) {
     // Treat HarfBuzz hb_position_t as 16.16 fixed-point.
@@ -378,21 +379,19 @@
     SkBidiIterator::Level fLevel;
 };
 
-class HbIcuScriptRunIterator final : public SkShaper::ScriptRunIterator {
+class SkUnicodeHbScriptRunIterator final: public SkShaper::ScriptRunIterator {
 public:
-    HbIcuScriptRunIterator(const char* utf8, size_t utf8Bytes)
-        : fCurrent(utf8), fBegin(utf8), fEnd(fCurrent + utf8Bytes)
+    SkUnicodeHbScriptRunIterator(SkUnicodeScript script, const char* utf8, size_t utf8Bytes)
+        : fScript(std::move(script))
+        , fCurrent(utf8), fBegin(utf8), fEnd(fCurrent + utf8Bytes)
         , fCurrentScript(HB_SCRIPT_UNKNOWN)
     {}
-    static hb_script_t hb_script_from_icu(SkUnichar u) {
-        UErrorCode status = U_ZERO_ERROR;
-        UScriptCode scriptCode = uscript_getScript(u, &status);
-
-        if (U_FAILURE (status)) {
+    hb_script_t hb_script_from_icu(SkUnichar u) {
+        SkScriptIterator::ScriptID scriptId;
+        if (!fScript->getScript(u, &scriptId)) {
             return HB_SCRIPT_UNKNOWN;
         }
-
-        return hb_icu_script_to_script(scriptCode);
+        return hb_icu_script_to_script((UScriptCode)scriptId);
     }
     void consume() override {
         SkASSERT(fCurrent < fEnd);
@@ -428,6 +427,7 @@
         return SkSetFourByteTag(HB_UNTAG(fCurrentScript));
     }
 private:
+    SkUnicodeScript fScript;
     char const * fCurrent;
     char const * const fBegin;
     char const * const fEnd;
@@ -804,7 +804,9 @@
         return;
     }
 
-    std::unique_ptr<ScriptRunIterator> script(MakeHbIcuScriptRunIterator(utf8, utf8Bytes));
+    std::unique_ptr<ScriptRunIterator> script(MakeSkUnicodeHbScriptRunIterator(fUnicode.get(),
+                                                                                       utf8,
+                                                                                       utf8Bytes));
     if (!script) {
         return;
     }
@@ -1400,12 +1402,13 @@
 std::unique_ptr<SkShaper::BiDiRunIterator>
 SkShaper::MakeIcuBiDiRunIterator(const char* utf8, size_t utf8Bytes, uint8_t bidiLevel) {
     auto unicode = SkUnicode::Make();
-    std::unique_ptr<SkShaper::BiDiRunIterator> bidi =
-        SkShaper::MakeSkUnicodeBidiRunIterator(unicode.get(),
-                                               utf8,
-                                               utf8Bytes,
-                                               bidiLevel);
-    return bidi;
+    if (!unicode) {
+        return nullptr;
+    }
+    return SkShaper::MakeSkUnicodeBidiRunIterator(unicode.get(),
+                                                  utf8,
+                                                  utf8Bytes,
+                                                  bidiLevel);
 }
 
 std::unique_ptr<SkShaper::BiDiRunIterator>
@@ -1438,7 +1441,20 @@
 
 std::unique_ptr<SkShaper::ScriptRunIterator>
 SkShaper::MakeHbIcuScriptRunIterator(const char* utf8, size_t utf8Bytes) {
-    return std::make_unique<HbIcuScriptRunIterator>(utf8, utf8Bytes);
+    auto unicode = SkUnicode::Make();
+    if (!unicode) {
+        return nullptr;
+    }
+    return SkShaper::MakeSkUnicodeHbScriptRunIterator(unicode.get(), utf8, utf8Bytes);
+}
+
+std::unique_ptr<SkShaper::ScriptRunIterator>
+SkShaper::MakeSkUnicodeHbScriptRunIterator(SkUnicode* unicode, const char* utf8, size_t utf8Bytes) {
+    auto script = unicode->makeScriptIterator();
+    if (!script) {
+        return nullptr;
+    }
+    return std::make_unique<SkUnicodeHbScriptRunIterator>(std::move(script), utf8, utf8Bytes);
 }
 
 std::unique_ptr<SkShaper> SkShaper::MakeShaperDrivenWrapper(sk_sp<SkFontMgr> fontmgr) {
diff --git a/modules/skshaper/src/SkUnicode.h b/modules/skshaper/src/SkUnicode.h
index 07b0904..0db2302 100644
--- a/modules/skshaper/src/SkUnicode.h
+++ b/modules/skshaper/src/SkUnicode.h
@@ -68,9 +68,15 @@
     virtual bool setText(const char utftext8[], int utf8Units) = 0;
 };
 
+class SKUNICODE_API SkScriptIterator {
+ public:
+    typedef uint32_t ScriptID;
+    virtual ~SkScriptIterator() = default;
+    virtual bool getScript(SkUnichar u, ScriptID* script) = 0;
+};
+
 class SKUNICODE_API SkUnicode {
     public:
-        typedef uint32_t ScriptID;
         typedef uint32_t CombiningClass;
         typedef uint32_t GeneralCategory;
         enum class TextDirection {
@@ -116,6 +122,7 @@
             (const char text[], int count, SkBidiIterator::Direction) = 0;
         virtual std::unique_ptr<SkBreakIterator> makeBreakIterator
             (const char locale[], BreakType breakType) = 0;
+        virtual std::unique_ptr<SkScriptIterator> makeScriptIterator() = 0;
 
         // High level methods (that we actually use somewhere=SkParagraph)
         virtual bool getBidiRegions
diff --git a/modules/skshaper/src/SkUnicode_icu.cpp b/modules/skshaper/src/SkUnicode_icu.cpp
index 7eb0f86..806900d 100644
--- a/modules/skshaper/src/SkUnicode_icu.cpp
+++ b/modules/skshaper/src/SkUnicode_icu.cpp
@@ -11,6 +11,7 @@
 #include "src/utils/SkUTF.h"
 #include <unicode/ubidi.h>
 #include <unicode/ubrk.h>
+#include <unicode/uscript.h>
 #include <unicode/ustring.h>
 #include <unicode/utext.h>
 #include <unicode/utypes.h>
@@ -179,6 +180,25 @@
     }
 };
 
+class SkScriptIterator_icu : public SkScriptIterator {
+ public:
+   bool getScript(SkUnichar u, ScriptID* script) override {
+        UErrorCode status = U_ZERO_ERROR;
+        UScriptCode scriptCode = uscript_getScript(u, &status);
+        if (U_FAILURE (status)) {
+            return false;
+        }
+        if (script) {
+            *script = (ScriptID)scriptCode;
+        }
+        return true;
+   }
+
+   static std::unique_ptr<SkScriptIterator> makeScriptIterator() {
+        return std::unique_ptr<SkScriptIterator>(new SkScriptIterator_icu());
+   }
+};
+
 class SkUnicode_icu : public SkUnicode {
 
     static UBreakIteratorType convertType(BreakType type) {
@@ -376,6 +396,9 @@
                                                        BreakType breakType) override {
         return SkBreakIterator_icu::makeUtf8BreakIterator(locale, breakType);
     }
+    std::unique_ptr<SkScriptIterator> makeScriptIterator() override {
+        return SkScriptIterator_icu::makeScriptIterator();
+    }
 
     // TODO: Use ICU data file to detect controls and whitespaces
     bool isControl(SkUnichar utf8) override {