ICU on windows

Change-Id: Ib1a2f017d96c5157c60d512332fddfef77c5ae8e
Reviewed-on: https://skia-review.googlesource.com/103001
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Hal Canary <halcanary@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 3d3a9bf..b214a25 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1609,8 +1609,8 @@
     }
   }
 
-  # We can't yet build ICU on iOS or Windows.
-  if (!is_ios && !is_win && target_cpu != "wasm") {
+  # TODO(halcanary): Build ICU on iOS.
+  if (!is_ios && target_cpu != "wasm") {
     test_app("sktexttopdf-hb") {
       sources = [
         "tools/shape/SkShaper_harfbuzz.cpp",
@@ -1794,9 +1794,8 @@
   if (!is_win && skia_enable_gpu) {
     test_lib("skqp_lib") {
       public_include_dirs = [ "tools/skqp" ]
-      defines = [
-        "SK_SKQP_GLOBAL_ERROR_TOLERANCE=$skia_skqp_global_error_tolerance"
-      ]
+      defines =
+          [ "SK_SKQP_GLOBAL_ERROR_TOLERANCE=$skia_skqp_global_error_tolerance" ]
       if (skia_skqp_enable_driver_correctness_workarounds) {
         defines += [ "SK_SKQP_ENABLE_DRIVER_CORRECTNESS_WORKAROUNDS" ]
       }
diff --git a/third_party/icu/BUILD.gn b/third_party/icu/BUILD.gn
index 68a5923..6b2482f 100644
--- a/third_party/icu/BUILD.gn
+++ b/third_party/icu/BUILD.gn
@@ -16,16 +16,17 @@
   }
 } else {
   third_party("icu") {
-    public_include_dirs = [ "../externals/icu/source/common" ]
+    public_include_dirs = [
+      "../externals/icu/source/common",
+      ".",
+    ]
     public_defines = [ "U_USING_ICU_NAMESPACE=0" ]
     configs -= [ "//gn:no_rtti" ]
-    if (!is_win) {
-      libs = [ "dl" ]
-    }
     defines = [
       # http://userguide.icu-project.org/howtouseicu
       "U_COMMON_IMPLEMENTATION",
       "U_STATIC_IMPLEMENTATION",
+      "U_ENABLE_DYLOAD=0",
     ]
     sources = [
       "../externals/icu/source/common/appendable.cpp",
@@ -209,6 +210,30 @@
       "../externals/icu/source/common/uvectr64.cpp",
       "../externals/icu/source/common/wintz.c",
     ]
-    sources += [ "../externals/icu/$current_os/icudtl_dat.S" ]
+    if (is_win) {
+      deps = [
+        ":icudata",
+      ]
+      public_defines += [
+        "U_NOEXCEPT=",
+        "U_STATIC_IMPLEMENTATION",
+      ]
+      libs = [ "Advapi32.lib" ]
+      sources += [ "../externals/icu/source/stubdata/stubdata.c" ]
+    } else {
+      sources += [ "../externals/icu/$current_os/icudtl_dat.S" ]
+      libs = [ "dl" ]
+    }
+  }
+  if (is_win) {
+    copy("icudata") {
+      sources = [
+        "../externals/icu/windows/icudt.dll",
+      ]
+      outputs = [
+        "$root_out_dir/icudt.dll",
+      ]
+      data = outputs
+    }
   }
 }
diff --git a/third_party/icu/SkLoadICU.h b/third_party/icu/SkLoadICU.h
new file mode 100644
index 0000000..e28d629
--- /dev/null
+++ b/third_party/icu/SkLoadICU.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef load_icu_DEFINED
+#define load_icu_DEFINED
+
+#include "SkTypes.h"
+
+#ifdef SK_BUILD_FOR_WIN
+
+#include "../private/SkLeanWindows.h"
+
+#include "unicode/uvernum.h"
+#include "unicode/udata.h"
+
+#define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat"
+#define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll"
+
+inline void SkLoadICU() {
+    HMODULE module = LoadLibrary(ICU_UTIL_DATA_SHARED_MODULE_NAME);
+    if (!module) {
+        SK_ABORT("Failed to load " ICU_UTIL_DATA_SHARED_MODULE_NAME "\n");
+    }
+    FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL);
+    if (!addr) {
+        SK_ABORT("Symbol " ICU_UTIL_DATA_SYMBOL " missing in "
+                 ICU_UTIL_DATA_SHARED_MODULE_NAME ".\n");
+    }
+    UErrorCode err = U_ZERO_ERROR;
+    udata_setCommonData(reinterpret_cast<void*>(addr), &err);
+    if (err != U_ZERO_ERROR) {
+        SkDebugf("udata_setCommonData() returned %d.\n", (int)err);
+        SK_ABORT("");
+    }
+    udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
+    if (err != U_ZERO_ERROR) {
+        SkDebugf("udata_setFileAccess() returned %d.\n", (int)err);
+        SK_ABORT("");
+    }
+}
+
+#undef ICU_UTIL_DATA_SHARED_MODULE_NAME
+#undef ICU_UTIL_DATA_SYMBOL
+
+#else
+inline void SkLoadICU() {}
+#endif  // SK_BUILD_FOR_WIN
+#endif  // load_icu_DEFINED
+
diff --git a/tools/shape/SkShaper_harfbuzz.cpp b/tools/shape/SkShaper_harfbuzz.cpp
index c5f437e..40414d9 100644
--- a/tools/shape/SkShaper_harfbuzz.cpp
+++ b/tools/shape/SkShaper_harfbuzz.cpp
@@ -15,6 +15,8 @@
 #include <unicode/uscript.h>
 
 #include "SkFontMgr.h"
+#include "SkLoadICU.h"
+#include "SkOnce.h"
 #include "SkShaper.h"
 #include "SkStream.h"
 #include "SkTDPQueue.h"
@@ -448,6 +450,9 @@
 };
 
 SkShaper::SkShaper(sk_sp<SkTypeface> tf) : fImpl(new Impl) {
+    SkOnce once;
+    once([] { SkLoadICU(); });
+
     fImpl->fTypeface = tf ? std::move(tf) : SkTypeface::MakeDefault();
     fImpl->fHarfBuzzFont = create_hb_font(fImpl->fTypeface.get());
     SkASSERT(fImpl->fHarfBuzzFont);