Work around macOS 11 variation issue.

On macOS 11 cloning a system font with an opsz axis and not changing the
value of the opsz axis (either by setting it to the same value or not
specifying it at all) when setting a variation causes the variation to
be set but the cloned font will still compare CFEqual to the original
font. Work around this by setting the opsz to something which isn't the
desired value before setting the entire desired variation.

This may also incidentally improve the situation for fonts created from
data on macOS 10.15, since a similar issue occurs and the same work
around seems to apply.

Bug: skia:10968
Change-Id: I2240f905644b753e2389446d248c27b1e62cdcd1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/370917
Reviewed-by: Herb Derby <herb@google.com>
Reviewed-by: Dominik Röttsches <drott@chromium.org>
Commit-Queue: Ben Wagner <bungeman@google.com>
diff --git a/src/ports/SkFontMgr_mac_ct.cpp b/src/ports/SkFontMgr_mac_ct.cpp
index 6d608e9..0a50238 100644
--- a/src/ports/SkFontMgr_mac_ct.cpp
+++ b/src/ports/SkFontMgr_mac_ct.cpp
@@ -267,7 +267,7 @@
                 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
         CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
     }
-    return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
+    return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), nullptr, opsz };
 }
 
 static sk_sp<SkData> skdata_from_skstreamasset(std::unique_ptr<SkStreamAsset> stream) {
@@ -615,13 +615,13 @@
         CTFontVariation ctVariation = SkCTVariationFromSkFontArguments(ct.get(), args);
 
         SkUniqueCFRef<CTFontRef> ctVariant;
-        if (ctVariation.dict) {
+        if (ctVariation.variation) {
             SkUniqueCFRef<CFMutableDictionaryRef> attributes(
                     CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
                                               &kCFTypeDictionaryKeyCallBacks,
                                               &kCFTypeDictionaryValueCallBacks));
             CFDictionaryAddValue(attributes.get(),
-                                 kCTFontVariationAttribute, ctVariation.dict.get());
+                                 kCTFontVariationAttribute, ctVariation.variation.get());
             SkUniqueCFRef<CTFontDescriptorRef> varDesc(
                     CTFontDescriptorCreateWithAttributes(attributes.get()));
             ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get()));
@@ -653,13 +653,13 @@
         CTFontVariation ctVariation = ctvariation_from_skfontdata(ct.get(), fontData.get());
 
         SkUniqueCFRef<CTFontRef> ctVariant;
-        if (ctVariation.dict) {
+        if (ctVariation.variation) {
             SkUniqueCFRef<CFMutableDictionaryRef> attributes(
                     CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
                                               &kCFTypeDictionaryKeyCallBacks,
                                               &kCFTypeDictionaryValueCallBacks));
             CFDictionaryAddValue(attributes.get(),
-                                 kCTFontVariationAttribute, ctVariation.dict.get());
+                                 kCTFontVariationAttribute, ctVariation.variation.get());
             SkUniqueCFRef<CTFontDescriptorRef> varDesc(
                     CTFontDescriptorCreateWithAttributes(attributes.get()));
             ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get()));
diff --git a/src/ports/SkTypeface_mac_ct.cpp b/src/ports/SkTypeface_mac_ct.cpp
index 94903f4..3847465 100644
--- a/src/ports/SkTypeface_mac_ct.cpp
+++ b/src/ports/SkTypeface_mac_ct.cpp
@@ -1117,14 +1117,15 @@
     CFIndex axisCount = CFArrayGetCount(ctAxes.get());
 
     // On 10.12 and later, this only returns non-default variations.
-    SkUniqueCFRef<CFDictionaryRef> ctVariation(CTFontCopyVariation(ct));
+    SkUniqueCFRef<CFDictionaryRef> oldCtVariation(CTFontCopyVariation(ct));
 
     const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
 
-    SkUniqueCFRef<CFMutableDictionaryRef> dict(
+    SkUniqueCFRef<CFMutableDictionaryRef> newCtVariation(
             CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
                                       &kCFTypeDictionaryKeyCallBacks,
                                       &kCFTypeDictionaryValueCallBacks));
+    SkUniqueCFRef<CFMutableDictionaryRef> wrongOpszVariation;
 
     for (int i = 0; i < axisCount; ++i) {
         CFDictionaryRef axisInfoDict;
@@ -1158,12 +1159,16 @@
         double value = defDouble;
 
         // Then the current value.
-        if (ctVariation) {
-            CFTypeRef currentValue = CFDictionaryGetValue(ctVariation.get(), tagNumber);
-            if (currentValue) {
-                if (!SkCFNumberDynamicCast(currentValue, &value, nullptr, "Variation value")) {
+        bool haveCurrentDouble = false;
+        double currentDouble = 0;
+        if (oldCtVariation) {
+            CFTypeRef currentNumber = CFDictionaryGetValue(oldCtVariation.get(), tagNumber);
+            if (currentNumber) {
+                if (!SkCFNumberDynamicCast(currentNumber, &value, nullptr, "Variation value")) {
                     return CTFontVariation();
                 }
+                currentDouble = value;
+                haveCurrentDouble = true;
             }
         }
 
@@ -1181,28 +1186,66 @@
         }
         if (tagLong == opszTag) {
             opsz.value = value;
+            if (haveCurrentDouble && value == currentDouble) {
+                // Calculate a value strictly in range but different from currentValue.
+                double wrongOpszDouble = ((maxDouble - minDouble) / 2.0) + minDouble;
+                if (wrongOpszDouble == currentDouble) {
+                    wrongOpszDouble = ((maxDouble - minDouble) / 4.0) + minDouble;
+                }
+                wrongOpszVariation.reset(
+                    CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                              &kCFTypeDictionaryKeyCallBacks,
+                                              &kCFTypeDictionaryValueCallBacks));
+                SkUniqueCFRef<CFNumberRef> wrongOpszNumber(
+                    CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &wrongOpszDouble));
+                CFDictionarySetValue(wrongOpszVariation.get(), tagNumber, wrongOpszNumber.get());
+            }
         }
         SkUniqueCFRef<CFNumberRef> valueNumber(
             CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
-        CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
+        CFDictionaryAddValue(newCtVariation.get(), tagNumber, valueNumber.get());
     }
-    return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
+    return { SkUniqueCFRef<CFDictionaryRef>(std::move(newCtVariation)),
+             SkUniqueCFRef<CFDictionaryRef>(std::move(wrongOpszVariation)),
+             opsz };
 }
 
 sk_sp<SkTypeface> SkTypeface_Mac::onMakeClone(const SkFontArguments& args) const {
     CTFontVariation ctVariation = SkCTVariationFromSkFontArguments(fFontRef.get(), args);
 
     SkUniqueCFRef<CTFontRef> ctVariant;
-    if (ctVariation.dict) {
+    if (ctVariation.variation) {
         SkUniqueCFRef<CFMutableDictionaryRef> attributes(
                 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
                                           &kCFTypeDictionaryKeyCallBacks,
                                           &kCFTypeDictionaryValueCallBacks));
-        CFDictionaryAddValue(attributes.get(),
-                             kCTFontVariationAttribute, ctVariation.dict.get());
+
+        CTFontRef ctFont = fFontRef.get();
+        SkUniqueCFRef<CTFontRef> wrongOpszFont;
+        if (ctVariation.wrongOpszVariation) {
+            // On macOS 11 cloning a system font with an opsz axis and not changing the
+            // value of the opsz axis (either by setting it to the same value or not
+            // specifying it at all) when setting a variation causes the variation to
+            // be set but the cloned font will still compare CFEqual to the original
+            // font. Work around this by setting the opsz to something which isn't the
+            // desired value before setting the entire desired variation.
+            //
+            // A similar issue occurs with fonts from data on macOS 10.15 and the same
+            // work around seems to apply. This is less noticeable though since CFEqual
+            // isn't used on these fonts.
+            CFDictionarySetValue(attributes.get(),
+                                 kCTFontVariationAttribute, ctVariation.wrongOpszVariation.get());
+            SkUniqueCFRef<CTFontDescriptorRef> varDesc(
+                CTFontDescriptorCreateWithAttributes(attributes.get()));
+            wrongOpszFont.reset(CTFontCreateCopyWithAttributes(ctFont, 0, nullptr, varDesc.get()));
+            ctFont = wrongOpszFont.get();
+        }
+
+        CFDictionarySetValue(attributes.get(),
+                             kCTFontVariationAttribute, ctVariation.variation.get());
         SkUniqueCFRef<CTFontDescriptorRef> varDesc(
                 CTFontDescriptorCreateWithAttributes(attributes.get()));
-        ctVariant.reset(CTFontCreateCopyWithAttributes(fFontRef.get(), 0, nullptr, varDesc.get()));
+        ctVariant.reset(CTFontCreateCopyWithAttributes(ctFont, 0, nullptr, varDesc.get()));
     } else {
         ctVariant.reset((CTFontRef)CFRetain(fFontRef.get()));
     }
diff --git a/src/ports/SkTypeface_mac_ct.h b/src/ports/SkTypeface_mac_ct.h
index f094ffa..3ea12f4 100644
--- a/src/ports/SkTypeface_mac_ct.h
+++ b/src/ports/SkTypeface_mac_ct.h
@@ -50,7 +50,8 @@
 };
 
 struct CTFontVariation {
-    SkUniqueCFRef<CFDictionaryRef> dict;
+    SkUniqueCFRef<CFDictionaryRef> variation;
+    SkUniqueCFRef<CFDictionaryRef> wrongOpszVariation;
     OpszVariation opsz;
 };