Use FontWeight symbols when available on Mac.

Newer versions of macOS have NSFontWeightXXX symbols and iOS has
UIFontWeightXXX symbols to provide the conversion between [-1, 1]
CTFontDescriptor weights and [0, 1000] CSS weights. Currently the
values these symbols provide are hard coded (for expediency), but the
symbols should be used when available since the values may change
without notice.

Change-Id: I0875c6a96c5b14e456f91bccf7a84f0306a3d63a
Reviewed-on: https://skia-review.googlesource.com/10163
Reviewed-by: Mike Klein <mtklein@chromium.org>
Commit-Queue: Ben Wagner <bungeman@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index af6bbda..3364148 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -700,6 +700,8 @@
       "src/ports/SkImageGeneratorCG.cpp",
     ]
     libs += [
+      # AppKit symbols NSFontWeightXXX may be dlsym'ed.
+      "AppKit.framework",
       "ApplicationServices.framework",
       "OpenGL.framework",
     ]
@@ -718,6 +720,9 @@
       "CoreText.framework",
       "ImageIO.framework",
       "MobileCoreServices.framework",
+
+      # UIKit symbols UIFontWeightXXX may be dlsym'ed.
+      "UIKit.framework",
     ]
   }
 
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index ca236a3..176a20c 100644
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -46,6 +46,8 @@
 #include "SkTypeface_mac.h"
 #include "SkUtils.h"
 
+#include <dlfcn.h>
+
 // Experimental code to use a global lock whenever we access CG, to see if this reduces
 // crashes in Chrome
 #define USE_GLOBAL_MUTEX_FOR_CG_ACCESS
@@ -311,6 +313,56 @@
     CGFloat operator()(CGFloat s) { return s; }
 };
 
+/** Returns the [-1, 1] CTFontDescriptor weights for the
+ *  <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
+ *
+ *  It is assumed that the values will be interpolated linearly between these points.
+ *  NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
+ *  The actual values appear to be stable, but they may change in the future without notice.
+ */
+static CGFloat(&get_NSFontWeight_mapping())[11] {
+
+    // Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
+#ifdef SK_BUILD_FOR_MAC
+#  define SK_KIT_FONT_WEIGHT_PREFIX "NS"
+#endif
+#ifdef SK_BUILD_FOR_IOS
+#  define SK_KIT_FONT_WEIGHT_PREFIX "UI"
+#endif
+    static constexpr struct {
+        CGFloat defaultValue;
+        const char* name;
+    } nsFontWeightLoaderInfos[] = {
+        { -0.80f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight" },
+        { -0.60f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin" },
+        { -0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight" },
+        {  0.00f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular" },
+        {  0.23f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium" },
+        {  0.30f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold" },
+        {  0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold" },
+        {  0.56f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy" },
+        {  0.62f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack" },
+    };
+
+    static_assert(SK_ARRAY_COUNT(nsFontWeightLoaderInfos) == 9, "");
+    static CGFloat nsFontWeights[11];
+    static SkOnce once;
+    once([&] {
+        size_t i = 0;
+        nsFontWeights[i++] = -1.00;
+        for (const auto& nsFontWeightLoaderInfo : nsFontWeightLoaderInfos) {
+            void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightLoaderInfo.name);
+            if (nsFontWeightValuePtr) {
+                nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
+            } else {
+                nsFontWeights[i++] = nsFontWeightLoaderInfo.defaultValue;
+            }
+        }
+        nsFontWeights[i++] = 1.00;
+    });
+    return nsFontWeights;
+}
+
 /** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
  *
  *  The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
@@ -322,31 +374,15 @@
     // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
     // However, on this end we can't tell, so this is ignored.
 
-    /** This mapping for native fonts is determined by running the following in an .mm file
-     *  #include <AppKit/AppKit>
-     *  printf("{  100, % #.2f },\n", NSFontWeightUltraLight);
-     *  printf("{  200, % #.2f },\n", NSFontWeightThin);
-     *  printf("{  300, % #.2f },\n", NSFontWeightLight);
-     *  printf("{  400, % #.2f },\n", NSFontWeightRegular);
-     *  printf("{  500, % #.2f },\n", NSFontWeightMedium);
-     *  printf("{  600, % #.2f },\n", NSFontWeightSemibold);
-     *  printf("{  700, % #.2f },\n", NSFontWeightBold);
-     *  printf("{  800, % #.2f },\n", NSFontWeightHeavy);
-     *  printf("{  900, % #.2f },\n", NSFontWeightBlack);
-     */
-    static constexpr Interpolator::Mapping nativeWeightMappings[] = {
-        {    0, -1.00 },
-        {  100, -0.80 },
-        {  200, -0.60 },
-        {  300, -0.40 },
-        {  400,  0.00 },
-        {  500,  0.23 },
-        {  600,  0.30 },
-        {  700,  0.40 },
-        {  800,  0.56 },
-        {  900,  0.62 },
-        { 1000,  1.00 },
-    };
+    static Interpolator::Mapping nativeWeightMappings[11];
+    static SkOnce once;
+    once([&] {
+        CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
+        for (int i = 0; i < 11; ++i) {
+            nativeWeightMappings[i].src_val = i * 100;
+            nativeWeightMappings[i].dst_val = nsFontWeights[i];
+        }
+    });
     static constexpr Interpolator nativeInterpolator(
             nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
 
@@ -385,31 +421,15 @@
     static constexpr Interpolator dataProviderInterpolator(
             dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
 
-    /** This mapping for native fonts is determined by running the following in an .mm file
-     *  #include <AppKit/AppKit>
-     *  printf("{ % #.2f,  100 },\n", NSFontWeightUltraLight);
-     *  printf("{ % #.2f,  200 },\n", NSFontWeightThin);
-     *  printf("{ % #.2f,  300 },\n", NSFontWeightLight);
-     *  printf("{ % #.2f,  400 },\n", NSFontWeightRegular);
-     *  printf("{ % #.2f,  500 },\n", NSFontWeightMedium);
-     *  printf("{ % #.2f,  600 },\n", NSFontWeightSemibold);
-     *  printf("{ % #.2f,  700 },\n", NSFontWeightBold);
-     *  printf("{ % #.2f,  800 },\n", NSFontWeightHeavy);
-     *  printf("{ % #.2f,  900 },\n", NSFontWeightBlack);
-     */
-    static constexpr Interpolator::Mapping nativeWeightMappings[] = {
-        { -1.00,    0 },
-        { -0.80,  100 },
-        { -0.60,  200 },
-        { -0.40,  300 },
-        {  0.00,  400 },
-        {  0.23,  500 },
-        {  0.30,  600 },
-        {  0.40,  700 },
-        {  0.56,  800 },
-        {  0.62,  900 },
-        {  1.00, 1000 },
-    };
+    static Interpolator::Mapping nativeWeightMappings[11];
+    static SkOnce once;
+    once([&] {
+        CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
+        for (int i = 0; i < 11; ++i) {
+            nativeWeightMappings[i].src_val = nsFontWeights[i];
+            nativeWeightMappings[i].dst_val = i * 100;
+        }
+    });
     static constexpr Interpolator nativeInterpolator(
             nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));