Ease HarfBuzz API change with feature detection

Skia has been using the not entirely public HarfBuzz subsetting API.
This API is changing for public release. In order to make the transition
from old to new build flags were added, which would require build
changes as HarfBuzz is updated downstream. Instead detect the existence
of the old or new API and use whichever is present automatically.

Change-Id: I0727f97ad7d394dfb24553076d4b383570cf0002
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/437121
Reviewed-by: Garret Rieger <grieger@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index f737ef0..aeb46b46 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1047,9 +1047,6 @@
   if (skia_use_icu && skia_use_harfbuzz && skia_pdf_subset_harfbuzz) {
     deps += [ "//third_party/harfbuzz" ]
     defines = [ "SK_PDF_USE_HARFBUZZ_SUBSET" ]
-    if (skia_subset_harfbuzz_new_api) {
-      defines += [ "SK_HARFBUZZ_SUBSET_NEW_API" ]
-    }
   } else if (skia_use_icu && skia_use_sfntly) {
     deps += [ "//third_party/sfntly" ]
     defines = [ "SK_PDF_USE_SFNTLY" ]
diff --git a/gn/gn_to_bp.py b/gn/gn_to_bp.py
index 9b2e999..a813c73 100755
--- a/gn/gn_to_bp.py
+++ b/gn/gn_to_bp.py
@@ -402,9 +402,6 @@
     # files.
     'target_cpu':                           '"none"',
 
-    # disable harfbuzz new subsetting api until android picks up harfbuzz 3.0.0
-    'skia_subset_harfbuzz_new_api':         'false',
-
     # Use the custom FontMgr, as the framework will handle fonts.
     'skia_enable_fontmgr_custom_directory': 'false',
     'skia_enable_fontmgr_custom_embedded':  'false',
diff --git a/gn/skia.gni b/gn/skia.gni
index fdb0935..199335d 100644
--- a/gn/skia.gni
+++ b/gn/skia.gni
@@ -99,7 +99,6 @@
 
 declare_args() {
   skia_pdf_subset_harfbuzz = skia_use_harfbuzz
-  skia_subset_harfbuzz_new_api = !is_fuchsia
 }
 
 declare_args() {
diff --git a/src/pdf/SkPDFSubsetFont.cpp b/src/pdf/SkPDFSubsetFont.cpp
index 3289940..2340a79 100644
--- a/src/pdf/SkPDFSubsetFont.cpp
+++ b/src/pdf/SkPDFSubsetFont.cpp
@@ -49,6 +49,37 @@
                                 blob.release());
 }
 
+template<typename...> using void_t = void;
+template<typename T, typename = void>
+struct SkPDFHarfBuzzSubset {
+    // This is the HarfBuzz 3.0 interface.
+    // hb_subset_flags_t does not exist in 2.0. It isn't dependent on T, so inline the value of
+    // HB_SUBSET_FLAGS_RETAIN_GIDS until 2.0 is no longer supported.
+    static HBFace Make(T input, hb_face_t* face) {
+        // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY.
+        // If it isn't known if a font is 'tricky', retain the hints.
+        hb_subset_input_set_flags(input, 2/*HB_SUBSET_FLAGS_RETAIN_GIDS*/);
+        return HBFace(hb_subset_or_fail(face, input));
+    }
+};
+template<typename T>
+struct SkPDFHarfBuzzSubset<T, void_t<
+    decltype(hb_subset_input_set_retain_gids(std::declval<T>(), std::declval<bool>())),
+    decltype(hb_subset_input_set_drop_hints(std::declval<T>(), std::declval<bool>())),
+    decltype(hb_subset(std::declval<hb_face_t*>(), std::declval<T>()))
+    >>
+{
+    // This is the HarfBuzz 2.0 (non-public) interface, used if it exists.
+    // This code should be removed as soon as all users are migrated to the newer API.
+    static HBFace Make(T input, hb_face_t* face) {
+        hb_subset_input_set_retain_gids(input, true);
+        // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY.
+        // If it isn't known if a font is 'tricky', retain the hints.
+        hb_subset_input_set_drop_hints(input, false);
+        return HBFace(hb_subset(face, input));
+    }
+};
+
 static sk_sp<SkData> subset_harfbuzz(sk_sp<SkData> fontData,
                                      const SkPDFGlyphUse& glyphUsage,
                                      int ttcIndex) {
@@ -71,20 +102,9 @@
     hb_set_t* glyphs = hb_subset_input_glyph_set(input.get());
     glyphUsage.getSetValues([&glyphs](unsigned gid) { hb_set_add(glyphs, gid);});
 
-#if defined(SK_HARFBUZZ_SUBSET_NEW_API)
-    // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY.
-    // If it isn't known if a font is 'tricky', retain the hints.
-    hb_subset_input_set_flags(input.get(), HB_SUBSET_FLAGS_RETAIN_GIDS);
-    HBFace subset(hb_subset_or_fail(face.get(), input.get()));
-#else
-    hb_subset_input_set_retain_gids(input.get(), true);
-    // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY.
-    // If it isn't known if a font is 'tricky', retain the hints.
-    hb_subset_input_set_drop_hints(input.get(), false);
-    HBFace subset(hb_subset(face.get(), input.get()));
-#endif
+    HBFace subset = SkPDFHarfBuzzSubset<hb_subset_input_t*>::Make(input.get(), face.get());
     if (!subset) {
-      return nullptr;
+        return nullptr;
     }
     HBBlob result(hb_face_reference_blob(subset.get()));
     return to_data(std::move(result));
diff --git a/third_party/harfbuzz/config-override.h b/third_party/harfbuzz/config-override.h
index 62a7af4..899a594 100644
--- a/third_party/harfbuzz/config-override.h
+++ b/third_party/harfbuzz/config-override.h
@@ -4,6 +4,7 @@
 #include <mutex>
 
 using hb_mutex_impl_t = std::mutex;
+#define HB_MUTEX_IMPL_INIT      UNUSED
 #define hb_mutex_impl_init(M)   HB_STMT_START { new (M) hb_mutex_impl_t;  } HB_STMT_END
 #define hb_mutex_impl_lock(M)   (M)->lock ()
 #define hb_mutex_impl_unlock(M) (M)->unlock ()